<commit_before>/*
Copyright 2019 The Ceph-CSI 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.
*/
package util

import (
	"strings"
	"testing"
)

func TestRoundOffBytes(t *testing.T) {
	type args struct {
		bytes int64
	}
	tests := []struct {
		name string
		args args
		want int64
	}{
		{
			"1MiB conversions",
			args{
				bytes: 1048576,
			},
			1048576,
		},
		{
			"1000kiB conversion",
			args{
				bytes: 1000,
			},
			1048576, // equal to 1MiB
		},
		{
			"1.5Mib conversion",
			args{
				bytes: 1572864,
			},
			2097152, // equal to 2MiB
		},
		{
			"1.1MiB conversion",
			args{
				bytes: 1153434,
			},
			2097152, // equal to 2MiB
		},
		{
			"1.5GiB conversion",
			args{
				bytes: 1610612736,
			},
			2147483648, // equal to 2GiB
		},
		{
			"1.1GiB conversion",
			args{
				bytes: 1181116007,
			},
			2147483648, // equal to 2GiB
		},
	}
	for _, tt := range tests {
		ts := tt
		t.Run(ts.name, func(t *testing.T) {
			if got := RoundOffBytes(ts.args.bytes); got != ts.want {
				t.Errorf("RoundOffBytes() = %v, want %v", got, ts.want)
			}
		})
	}
}

func TestRoundOffVolSize(t *testing.T) {
	type args struct {
		size int64
	}
	tests := []struct {
		name string
		args args
		want int64
	}{
		{
			"1MiB conversions",
			args{
				size: 1048576,
			},
			1, // MiB
		},
		{
			"1000kiB conversion",
			args{
				size: 1000,
			},
			1, // MiB
		},
		{
			"1.5Mib conversion",
			args{
				size: 1572864,
			},
			2, // MiB
		},
		{
			"1.1MiB conversion",
			args{
				size: 1153434,
			},
			2, // MiB
		},
		{
			"1.5GiB conversion",
			args{
				size: 1610612736,
			},
			2048, // MiB
		},
		{
			"1.1GiB conversion",
			args{
				size: 1181116007,
			},
			2048, // MiB
		},
	}
	for _, tt := range tests {
		ts := tt
		t.Run(ts.name, func(t *testing.T) {
			if got := RoundOffVolSize(ts.args.size); got != ts.want {
				t.Errorf("RoundOffVolSize() = %v, want %v", got, ts.want)
			}
		})
	}
}

func TestGetKernelVersion(t *testing.T) {
	version, err := GetKernelVersion()
	if err != nil {
		t.Errorf("failed to get kernel version: %s", err)
	}
	if version == "" {
		t.Error("version is empty, this is unexpected?!")
	}
	if strings.HasSuffix(version, "\x00") {
		t.Error("version ends with \\x00 byte(s)")
	}
}

func TestMountOptionsAdd(t *testing.T) {
	moaTests := []struct {
		name         string
		mountOptions string
		option       []string
		result       string
	}{
		{
			"add option to empty string",
			"",
			[]string{"new_option"},
			"new_option",
		},
		{
			"add empty option to string",
			"orig_option",
			[]string{""},
			"orig_option",
		},
		{
			"add empty option to empty string",
			"",
			[]string{""},
			"",
		},
		{
			"add option to single option string",
			"orig_option",
			[]string{"new_option"},
			"orig_option,new_option",
		},
		{
			"add option to multi option string",
			"orig_option,2nd_option",
			[]string{"new_option"},
			"orig_option,2nd_option,new_option",
		},
		{
			"add redundant option to multi option string",
			"orig_option,2nd_option",
			[]string{"2nd_option"},
			"orig_option,2nd_option",
		},
		{
			"add option to multi option string starting with ,",
			",orig_option,2nd_option",
			[]string{"new_option"},
			"orig_option,2nd_option,new_option",
		},
		{
			"add option to multi option string with trailing ,",
			"orig_option,2nd_option,",
			[]string{"new_option"},
			"orig_option,2nd_option,new_option",
		},
		{
			"add options to multi option string",
			"orig_option,2nd_option,",
			[]string{"new_option", "another_option"},
			"orig_option,2nd_option,new_option,another_option",
		},
		{
			"add options (one redundant) to multi option string",
			"orig_option,2nd_option,",
			[]string{"new_option", "2nd_option", "another_option"},
			"orig_option,2nd_option,new_option,another_option",
		},
	}

	for _, moaTest := range moaTests {
		mt := moaTest
		t.Run(moaTest.name, func(t *testing.T) {
			result := MountOptionsAdd(mt.mountOptions, mt.option...)
			if result != mt.result {
				t.Errorf("MountOptionsAdd(): %v, want %v", result, mt.result)
			}
		})
	}
}
func TestCheckKernelSupport(t *testing.T) {
	supportsQuota := []string{
		"4.17.0",
		"5.0.0",
		"4.17.0-rc1",
		"4.18.0-80.el8",
		"3.10.0-1062.el7.x86_64",     // 1st backport
		"3.10.0-1062.4.1.el7.x86_64", // updated backport
	}

	noQuota := []string{
		"2.6.32-754.15.3.el6.x86_64", // too old
		"3.10.0-123.el7.x86_64",      // too old for backport
		"3.10.0-1062.4.1.el8.x86_64", // nonexisting RHEL-8 kernel
		"3.11.0-123.el7.x86_64",      // nonexisting RHEL-7 kernel
	}

	quotaSupport := []KernelVersion{
		{4, 17, 0, 0, "", false},       // standard 4.17+ versions
		{3, 10, 0, 1062, ".el7", true}, // RHEL-7.7
	}
	for _, kernel := range supportsQuota {
		ok := CheckKernelSupport(kernel, quotaSupport)
		if !ok {
			t.Errorf("support expected for %s", kernel)
		}
	}

	for _, kernel := range noQuota {
		ok := CheckKernelSupport(kernel, quotaSupport)
		if ok {
			t.Errorf("no support expected for %s", kernel)
		}
	}

	supportsDeepFlatten := []string{
		"5.2.0",
		"5.3.0",
	}

	noDeepFlatten := []string{
		"4.18.0",                     // too old
		"3.10.0-123.el7.x86_64",      // too old for backport
		"3.10.0-1062.4.1.el8.x86_64", // nonexisting RHEL-8 kernel
		"3.11.0-123.el7.x86_64",      // nonexisting RHEL-7 kernel
	}

	deepFlattenSupport := []KernelVersion{
		{5, 2, 0, 0, "", false}, // standard 5.2+ versions
	}
	for _, kernel := range supportsDeepFlatten {
		ok := CheckKernelSupport(kernel, deepFlattenSupport)
		if !ok {
			t.Errorf("support expected for %s", kernel)
		}
	}

	for _, kernel := range noDeepFlatten {
		ok := CheckKernelSupport(kernel, deepFlattenSupport)
		if ok {
			t.Errorf("no support expected for %s", kernel)
		}
	}
}
<commit_msg>util: update unit testing for deep flatten<commit_after>/*
Copyright 2019 The Ceph-CSI 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.
*/
package util

import (
	"strings"
	"testing"
)

func TestRoundOffBytes(t *testing.T) {
	type args struct {
		bytes int64
	}
	tests := []struct {
		name string
		args args
		want int64
	}{
		{
			"1MiB conversions",
			args{
				bytes: 1048576,
			},
			1048576,
		},
		{
			"1000kiB conversion",
			args{
				bytes: 1000,
			},
			1048576, // equal to 1MiB
		},
		{
			"1.5Mib conversion",
			args{
				bytes: 1572864,
			},
			2097152, // equal to 2MiB
		},
		{
			"1.1MiB conversion",
			args{
				bytes: 1153434,
			},
			2097152, // equal to 2MiB
		},
		{
			"1.5GiB conversion",
			args{
				bytes: 1610612736,
			},
			2147483648, // equal to 2GiB
		},
		{
			"1.1GiB conversion",
			args{
				bytes: 1181116007,
			},
			2147483648, // equal to 2GiB
		},
	}
	for _, tt := range tests {
		ts := tt
		t.Run(ts.name, func(t *testing.T) {
			if got := RoundOffBytes(ts.args.bytes); got != ts.want {
				t.Errorf("RoundOffBytes() = %v, want %v", got, ts.want)
			}
		})
	}
}

func TestRoundOffVolSize(t *testing.T) {
	type args struct {
		size int64
	}
	tests := []struct {
		name string
		args args
		want int64
	}{
		{
			"1MiB conversions",
			args{
				size: 1048576,
			},
			1, // MiB
		},
		{
			"1000kiB conversion",
			args{
				size: 1000,
			},
			1, // MiB
		},
		{
			"1.5Mib conversion",
			args{
				size: 1572864,
			},
			2, // MiB
		},
		{
			"1.1MiB conversion",
			args{
				size: 1153434,
			},
			2, // MiB
		},
		{
			"1.5GiB conversion",
			args{
				size: 1610612736,
			},
			2048, // MiB
		},
		{
			"1.1GiB conversion",
			args{
				size: 1181116007,
			},
			2048, // MiB
		},
	}
	for _, tt := range tests {
		ts := tt
		t.Run(ts.name, func(t *testing.T) {
			if got := RoundOffVolSize(ts.args.size); got != ts.want {
				t.Errorf("RoundOffVolSize() = %v, want %v", got, ts.want)
			}
		})
	}
}

func TestGetKernelVersion(t *testing.T) {
	version, err := GetKernelVersion()
	if err != nil {
		t.Errorf("failed to get kernel version: %s", err)
	}
	if version == "" {
		t.Error("version is empty, this is unexpected?!")
	}
	if strings.HasSuffix(version, "\x00") {
		t.Error("version ends with \\x00 byte(s)")
	}
}

func TestMountOptionsAdd(t *testing.T) {
	moaTests := []struct {
		name         string
		mountOptions string
		option       []string
		result       string
	}{
		{
			"add option to empty string",
			"",
			[]string{"new_option"},
			"new_option",
		},
		{
			"add empty option to string",
			"orig_option",
			[]string{""},
			"orig_option",
		},
		{
			"add empty option to empty string",
			"",
			[]string{""},
			"",
		},
		{
			"add option to single option string",
			"orig_option",
			[]string{"new_option"},
			"orig_option,new_option",
		},
		{
			"add option to multi option string",
			"orig_option,2nd_option",
			[]string{"new_option"},
			"orig_option,2nd_option,new_option",
		},
		{
			"add redundant option to multi option string",
			"orig_option,2nd_option",
			[]string{"2nd_option"},
			"orig_option,2nd_option",
		},
		{
			"add option to multi option string starting with ,",
			",orig_option,2nd_option",
			[]string{"new_option"},
			"orig_option,2nd_option,new_option",
		},
		{
			"add option to multi option string with trailing ,",
			"orig_option,2nd_option,",
			[]string{"new_option"},
			"orig_option,2nd_option,new_option",
		},
		{
			"add options to multi option string",
			"orig_option,2nd_option,",
			[]string{"new_option", "another_option"},
			"orig_option,2nd_option,new_option,another_option",
		},
		{
			"add options (one redundant) to multi option string",
			"orig_option,2nd_option,",
			[]string{"new_option", "2nd_option", "another_option"},
			"orig_option,2nd_option,new_option,another_option",
		},
	}

	for _, moaTest := range moaTests {
		mt := moaTest
		t.Run(moaTest.name, func(t *testing.T) {
			result := MountOptionsAdd(mt.mountOptions, mt.option...)
			if result != mt.result {
				t.Errorf("MountOptionsAdd(): %v, want %v", result, mt.result)
			}
		})
	}
}
func TestCheckKernelSupport(t *testing.T) {
	supportsQuota := []string{
		"4.17.0",
		"5.0.0",
		"4.17.0-rc1",
		"4.18.0-80.el8",
		"3.10.0-1062.el7.x86_64",     // 1st backport
		"3.10.0-1062.4.1.el7.x86_64", // updated backport
	}

	noQuota := []string{
		"2.6.32-754.15.3.el6.x86_64", // too old
		"3.10.0-123.el7.x86_64",      // too old for backport
		"3.10.0-1062.4.1.el8.x86_64", // nonexisting RHEL-8 kernel
		"3.11.0-123.el7.x86_64",      // nonexisting RHEL-7 kernel
	}

	quotaSupport := []KernelVersion{
		{4, 17, 0, 0, "", false},       // standard 4.17+ versions
		{3, 10, 0, 1062, ".el7", true}, // RHEL-7.7
	}
	for _, kernel := range supportsQuota {
		ok := CheckKernelSupport(kernel, quotaSupport)
		if !ok {
			t.Errorf("support expected for %s", kernel)
		}
	}

	for _, kernel := range noQuota {
		ok := CheckKernelSupport(kernel, quotaSupport)
		if ok {
			t.Errorf("no support expected for %s", kernel)
		}
	}

	supportsDeepFlatten := []string{
		"5.1.0", // 5.1+ supports deep-flatten
		"5.3.0",
		"4.18.0-193.9.1.el8_2.x86_64", // RHEL 8.2 kernel
	}

	noDeepFlatten := []string{
		"4.18.0",                     // too old
		"3.10.0-123.el7.x86_64",      // too old for backport
		"3.10.0-1062.4.1.el8.x86_64", // nonexisting RHEL-8 kernel
		"3.11.0-123.el7.x86_64",      // nonexisting RHEL-7 kernel
	}

	deepFlattenSupport := []KernelVersion{
		{5, 1, 0, 0, "", false},       // standard 5.1+ versions
		{4, 18, 0, 193, ".el8", true}, // RHEL 8.2 backport
	}
	for _, kernel := range supportsDeepFlatten {
		ok := CheckKernelSupport(kernel, deepFlattenSupport)
		if !ok {
			t.Errorf("support expected for %s", kernel)
		}
	}

	for _, kernel := range noDeepFlatten {
		ok := CheckKernelSupport(kernel, deepFlattenSupport)
		if ok {
			t.Errorf("no support expected for %s", kernel)
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>Finish interpolate.go<commit_after>package main

import (
	"fmt"
	"math"
)

func generateInitialConditions(leftEndPoint float64, rightEndPoint float64, numPoints int) ([]float64, []float64) {
	var deltaX = (rightEndPoint - leftEndPoint) / float64(numPoints)
	var xVals = make([]float64, numPoints+1)
	var yVals = make([]float64, numPoints+1)
	for i := 0; i <= numPoints; i++ {
		xVals[i] = leftEndPoint + float64(i)*deltaX
		yVals[i] = math.Sin(20 * xVals[i]) // Change test function here
	}
	return xVals, yVals
}

func printPolynomial(coeff []float64) {
	var degree = len(coeff) - 1
	fmt.Print(coeff[0])
	for i := 1; i <= degree; i++ {
		fmt.Print(" + ")
		fmt.Printf("%f", coeff[i])
		fmt.Print("x^", i)
	}
}

func main() {
	leftEndPoint := float64(0)
	rightEndPoint := float64(1)
	numPoints := int(6)
	xVals, yVals := generateInitialConditions(leftEndPoint, rightEndPoint, numPoints)
	// We follow the algorithm outlined on page 332
	coeff := yVals
	for j := 1; j <= numPoints; j++ {
		for i := numPoints; i >= j; i-- {
			coeff[i] = (coeff[i] - coeff[i-1]) / (xVals[i] - xVals[i-j])
		}
	}
	//fmt.Print("p(x) = ")
	printPolynomial(coeff)
}
<|endoftext|>


<commit_before>// Copyright 2016 Politecnico di Torino
//
// 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 router

//Sanity Check Packet -> minimum length and correct checksum
//decrement TTL and recompute packet checksum (l3 recompute checksum)

//lookup in the longest prefix matching table:
//destination ip address of the packet.

//LONGEST PREFIX MATCHING trivialimplementation

var RouterCode = `
#include <linux/ip.h>
#include <linux/bpf.h>

// #define BPF_TRACE
#undef BPF_TRACE

#define BPF_LOG
// #undef BPF_LOG

#define ROUTING_TABLE_DIM 10
#define ROUTER_PORT_N     10
#define ARP_TABLE_DIM     10

#define IP_TTL_OFFSET  8
#define IP_CSUM_OFFSET 10

#define ETH_DST_OFFSET  0
#define ETH_SRC_OFFSET  6
#define ETH_TYPE_OFFSET 12

/*Routing Table Entry*/
struct rt_entry{
  u32 network;  //network: e.g. 192.168.1.0
  u32 netmask;  //netmask: e.g. 255.255.255.0
  u32 port;     //port of the router
};

/*Router Port*/
struct r_port{
  u32 ip;       //ip addr : e.g. 192.168.1.254
  u32 netmask;  //netmask : e.g. 255.255.255.0
  u64 mac;      //mac addr: e.g. a1:b2:c3:ab:cd:ef
};

/*Arp Table Key*/
struct arp_table_key{
  u32 ip;       //ip addr : e.g. 192.168.1.2
  u32 port;     //port    : e.g. 1
};

/*
  The Routing table is implemented as an array of struct rt_entry (Routing Table Entry)
  the longest prefix matching algorithm (at least a simplified version)
  is implemented performing a bounded loop over the entries of the routing table.
  We assume that the control plane puts entry ordered from the longest netmask
  to the shortest one.
*/
BPF_TABLE("array", u32, struct rt_entry, routing_table, ROUTING_TABLE_DIM);

/*
  Router Port table provides a way to simulate the physical interface of the router
  The ip address is used to answer to the arp request (TO IMPLEMENT)
  The mac address is used as mac_scr for the outcoming packet on that interface,
  and as mac address contained in the arp reply
*/
BPF_TABLE("hash", u32, struct r_port, router_port, ROUTER_PORT_N);

/*
  We shold have an arp table for each port of the router?
  For now we assume to send packet exiting the router interfaces in broadcast
  (mac dst = ff:ff:ff:ff:ff:ff)

  How can we implement multiple arp tables?
  One possible implementation using one single map is the following
  key{ ip + port number } -> value {mac_address}
*/
BPF_TABLE("hash", struct arp_table_key, u64, arp_table, ARP_TABLE_DIM);

static int handle_rx(void *skb, struct metadata *md) {
  u8 *cursor = 0;
  struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));

  #ifdef BPF_TRACE
    bpf_trace_printk("[router]: in_ifc:%d\n", md->in_ifc);
    bpf_trace_printk("[router]: eth_type:%x mac_scr:%lx mac_dst:%lx\n",
      ethernet->type, ethernet->src, ethernet->dst);
  #endif

  //TODO
  //sanity check of the packet.
  //if something wrong -> DROP the packet

  struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));

  #ifdef BPF_TRACE
    bpf_trace_printk("[router]: ttl:%u ip_scr:%x ip_dst:%x \n", ip->ttl, ip->src, ip->dst);
    // bpf_trace_printk("[router]: (before) ttl: %d checksum: %x\n", ip->ttl, ip->hchecksum);
  #endif

  /*
    decrement TTL and recompute packet checksum (l3 recompute checksum).
    if ttl <= 1 DROP the packet.
    eventually send ICMP message for the packet dropped.
    (maybe to avoid for security reasons)
  */

  __u8 old_ttl = ip->ttl;
  __u8 new_ttl;

  if (old_ttl <= 1) {
    #ifdef BPF_TRACE
      bpf_trace_printk("[router]: packet DROP (ttl <= 1)\n");
    #endif
    return RX_DROP;
  }

  new_ttl = old_ttl - 1;
  bpf_l3_csum_replace(skb, sizeof(*ethernet) + IP_CSUM_OFFSET , old_ttl, new_ttl, sizeof(__u16));
  bpf_skb_store_bytes(skb, sizeof(*ethernet) + IP_TTL_OFFSET , &new_ttl, sizeof(old_ttl), 0);

  #ifdef BPF_TRACE
    // bpf_trace_printk("[router]: (after ) ttl: %d checksum: %x\n",ip->ttl,ip->hchecksum);
  #endif

  /*
    ROUTING ALGORITHM (simplified)

    for each item in the routing table (upbounded loop)
    apply the netmask on dst_ip_address
    (possible optimization, not recompute if at next iteration the netmask is the same)
    if masked address == network in the routing table
      1- change src mac to otuput port mac
      2- change dst mac to lookup arp table (or send to fffffffffffff)
      3- forward the packet to dst port
  */

  int i = 0;
  struct rt_entry *rt_entry_p = 0;

  u64 new_src_mac = 0;
  u64 new_dst_mac = 0;
  u32 out_port = 0;
  struct r_port *r_port_p = 0;

  #pragma unroll
  for (i = 0; i < ROUTING_TABLE_DIM; i++) {
    u32 t = i;
    rt_entry_p = routing_table.lookup(&t);
     if (rt_entry_p) {
      if ((ip->dst & rt_entry_p->netmask) == rt_entry_p->network) {
        goto FORWARD;
      }
    }
  }

DROP:
  #ifdef BPF_LOG
    bpf_trace_printk("[router]: in: %d out: -- DROP\n", md->in_ifc);
  #endif
  return RX_DROP;

FORWARD:
  //Select out interface
  out_port = rt_entry_p->port;
  if (out_port <= 0)
    goto DROP;

  #ifdef BPF_LOG
    bpf_trace_printk("[router]: routing table match (#%d) network: %x\n",
      i, rt_entry_p->network);
  #endif

  //change src mac
  r_port_p = router_port.lookup(&out_port);
  if (r_port_p) {
    new_src_mac = r_port_p->mac;
    bpf_skb_store_bytes(skb,ETH_SRC_OFFSET, &new_src_mac, 6, 0);
  }

  //change dst mac to ff:ff:ff:ff:ff:ff (TODO arp table)
  new_dst_mac = 0xffffffffffff;
  bpf_skb_store_bytes(skb, ETH_DST_OFFSET, &new_dst_mac, 6, 0);

  #ifdef BPF_TRACE
    bpf_trace_printk("[router]: eth_type:%x mac_scr:%lx mac_dst:%lx\n",
      ethernet->type, ethernet->src, ethernet->dst);
    bpf_trace_printk("[router]: out_ifc: %d\n", out_port);
  #endif

  #ifdef BPF_LOG
    bpf_trace_printk("[router]: in: %d out: %d REDIRECT\n", md->in_ifc, out_port);
  #endif

  pkt_redirect(skb,md,out_port);
  return RX_REDIRECT;

}
`
<commit_msg>iomodules/router: respond arp requests<commit_after>// Copyright 2016 Politecnico di Torino
//
// 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 router

//Sanity Check Packet -> minimum length and correct checksum
//decrement TTL and recompute packet checksum (l3 recompute checksum)

//lookup in the longest prefix matching table:
//destination ip address of the packet.

//LONGEST PREFIX MATCHING trivialimplementation

var RouterCode = `
#include <linux/ip.h>
#include <linux/bpf.h>
#include <linux/kernel.h>

// #define BPF_TRACE
#undef BPF_TRACE

#define BPF_LOG
// #undef BPF_LOG

#define ROUTING_TABLE_DIM 10
#define ROUTER_PORT_N     10
#define ARP_TABLE_DIM     10

#define IP_TTL_OFFSET  8
#define IP_CSUM_OFFSET 10

#define ETH_DST_OFFSET  0
#define ETH_SRC_OFFSET  6
#define ETH_TYPE_OFFSET 12

/*Routing Table Entry*/
struct rt_entry{
  u32 network;  //network: e.g. 192.168.1.0
  u32 netmask;  //netmask: e.g. 255.255.255.0
  u32 port;     //port of the router
};

/*Router Port*/
struct r_port{
  u32 ip;       //ip addr : e.g. 192.168.1.254
  u32 netmask;  //netmask : e.g. 255.255.255.0
  u64 mac;      //mac addr: e.g. a1:b2:c3:ab:cd:ef
};

/*Arp Table Key*/
struct arp_table_key{
  u32 ip;       //ip addr : e.g. 192.168.1.2
  u32 port;     //port    : e.g. 1
};

/*
  The Routing table is implemented as an array of struct rt_entry (Routing Table Entry)
  the longest prefix matching algorithm (at least a simplified version)
  is implemented performing a bounded loop over the entries of the routing table.
  We assume that the control plane puts entry ordered from the longest netmask
  to the shortest one.
*/
BPF_TABLE("array", u32, struct rt_entry, routing_table, ROUTING_TABLE_DIM);

/*
  Router Port table provides a way to simulate the physical interface of the router
  The ip address is used to answer to the arp request (TO IMPLEMENT)
  The mac address is used as mac_scr for the outcoming packet on that interface,
  and as mac address contained in the arp reply
*/
BPF_TABLE("hash", u32, struct r_port, router_port, ROUTER_PORT_N);

/*
  We shold have an arp table for each port of the router?
  For now we assume to send packet exiting the router interfaces in broadcast
  (mac dst = ff:ff:ff:ff:ff:ff)

  How can we implement multiple arp tables?
  One possible implementation using one single map is the following
  key{ ip + port number } -> value {mac_address}
*/
BPF_TABLE("hash", u32, u64, arp_table, ARP_TABLE_DIM);

static int handle_rx(void *skb, struct metadata *md) {
  u8 *cursor = 0;
  struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));

  #ifdef BPF_TRACE
    bpf_trace_printk("[router]: in_ifc:%d\n", md->in_ifc);
    bpf_trace_printk("[router]: eth_type:%x mac_scr:%lx mac_dst:%lx\n",
      ethernet->type, ethernet->src, ethernet->dst);
  #endif

  //TODO
  //sanity check of the packet.
  //if something wrong -> DROP the packet

  // is it an ipv4 packet?
  if (ethernet->type == 0x0800) {
    struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));

    #ifdef BPF_TRACE
      bpf_trace_printk("[router]: ttl:%u ip_scr:%x ip_dst:%x \n", ip->ttl, ip->src, ip->dst);
      // bpf_trace_printk("[router]: (before) ttl: %d checksum: %x\n", ip->ttl, ip->hchecksum);
    #endif

    /*
      decrement TTL and recompute packet checksum (l3 recompute checksum).
      if ttl <= 1 DROP the packet.
      eventually send ICMP message for the packet dropped.
      (maybe to avoid for security reasons)
    */

    __u8 old_ttl = ip->ttl;
    __u8 new_ttl;

    if (old_ttl <= 1) {
      #ifdef BPF_TRACE
        bpf_trace_printk("[router]: packet DROP (ttl <= 1)\n");
      #endif
      return RX_DROP;
    }

    new_ttl = old_ttl - 1;
    bpf_l3_csum_replace(skb, sizeof(*ethernet) + IP_CSUM_OFFSET , old_ttl, new_ttl, sizeof(__u16));
    bpf_skb_store_bytes(skb, sizeof(*ethernet) + IP_TTL_OFFSET , &new_ttl, sizeof(old_ttl), 0);

    #ifdef BPF_TRACE
      // bpf_trace_printk("[router]: (after ) ttl: %d checksum: %x\n",ip->ttl,ip->hchecksum);
    #endif

    /*
      ROUTING ALGORITHM (simplified)

      for each item in the routing table (upbounded loop)
      apply the netmask on dst_ip_address
      (possible optimization, not recompute if at next iteration the netmask is the same)
      if masked address == network in the routing table
        1- change src mac to otuput port mac
        2- change dst mac to lookup arp table (or send to fffffffffffff)
        3- forward the packet to dst port
    */

    int i = 0;
    struct rt_entry *rt_entry_p = 0;

    u64 new_src_mac = 0;
    u64 new_dst_mac = 0;
    u32 out_port = 0;
    struct r_port *r_port_p = 0;

    #pragma unroll
    for (i = 0; i < ROUTING_TABLE_DIM; i++) {
      u32 t = i;
      rt_entry_p = routing_table.lookup(&t);
       if (rt_entry_p) {
        if ((ip->dst & rt_entry_p->netmask) == rt_entry_p->network) {
          goto FORWARD;
        }
      }
    }

  DROP:
    #ifdef BPF_LOG
      bpf_trace_printk("[router]: in: %d out: -- DROP\n", md->in_ifc);
    #endif
    return RX_DROP;

  FORWARD:
    //Select out interface
    out_port = rt_entry_p->port;
    if (out_port <= 0)
      goto DROP;

    #ifdef BPF_LOG
      bpf_trace_printk("[router]: routing table match (#%d) network: %x\n",
        i, rt_entry_p->network);
    #endif

    //change src mac
    r_port_p = router_port.lookup(&out_port);
    if (r_port_p) {
      new_src_mac = cpu_to_be64(r_port_p->mac<<16);
      bpf_skb_store_bytes(skb,ETH_SRC_OFFSET, &new_src_mac, 6, 0);
    }

    //change dst mac to ff:ff:ff:ff:ff:ff (TODO arp table)
    new_dst_mac = 0xffffffffffff;
    bpf_skb_store_bytes(skb, ETH_DST_OFFSET, &new_dst_mac, 6, 0);

    #ifdef BPF_TRACE
      bpf_trace_printk("[router]: eth_type:%x mac_scr:%lx mac_dst:%lx\n",
        ethernet->type, ethernet->src, ethernet->dst);
      bpf_trace_printk("[router]: out_ifc: %d\n", out_port);
    #endif

    #ifdef BPF_LOG
      bpf_trace_printk("[router]: in: %d out: %d REDIRECT\n", md->in_ifc, out_port);
    #endif

    pkt_redirect(skb,md,out_port);
    return RX_REDIRECT;
  }
  else if(ethernet->type == 0x0806) { // is it ARP?
    struct arp_t *arp = cursor_advance(cursor, sizeof(*arp));
    if (arp->oper == 1) {	// arp request?
      bpf_trace_printk("[arp]: packet is arp request\n");

      struct r_port *port = router_port.lookup(&md->in_ifc);
      if (!port)
        return RX_DROP;
      if (arp->tpa == port->ip) {
        bpf_trace_printk("[arp]: Somebody is asking for my address\n");

        /* answer arp request */

        u16 two = cpu_to_be16(0x0002);
        u64 mymac = cpu_to_be64(port->mac<<16);
        u64 remotemac = arp->sha;
        remotemac = cpu_to_be64(remotemac<<16);
        u32 myip = cpu_to_be32(port->ip);
        u32 remoteip = arp->spa;
        remoteip = cpu_to_be32(remoteip);

        bpf_skb_store_bytes(skb, 0, &remotemac, 6, 0); // dst_mac
        bpf_skb_store_bytes(skb, 6, &mymac, 6, 0); // src_mac
        bpf_skb_store_bytes(skb, sizeof(*ethernet)+6, &two, 2, 0); // operation
        bpf_skb_store_bytes(skb, sizeof(*ethernet)+8, &mymac, 6, 0);// sha
        bpf_skb_store_bytes(skb, sizeof(*ethernet)+14, &myip, 4, 1);// spa
        bpf_skb_store_bytes(skb, sizeof(*ethernet)+18, &remotemac, 6, 0);// tha
        bpf_skb_store_bytes(skb, sizeof(*ethernet)+24, &remoteip, 4, 0);// tpa

        pkt_redirect(skb, md, md->in_ifc);

        return RX_REDIRECT;

      }
    }
    else if (arp->oper == 2) { //arp reply
      bpf_trace_printk("[arp]: packet is arp reply\n");

      struct r_port *port = router_port.lookup(&md->in_ifc);
      if (!port)
        return RX_DROP;
      if (arp->sha == port->mac && arp->spa == port->ip) {
        u64 mac_ = port->mac;
        u32 ip_ = port->ip;
        arp_table.update(&ip_, &mac_);
        return RX_DROP;
      }
    }
	}

  return RX_DROP;
}
`
<|endoftext|>


<commit_before>// Copyright 2013-2016 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package datetime

import (
	"log"
	"strings"
	"time"
)

var dateInputFormats = []string{
	"Mon, 02 Jan 2006 15:04:05 -0700",
	"Mon, 02 Jan 2006 15:04:05 -0700 MST",
	"Mon, 02 Jan 2006 15:04:05 -0700 (MST)",
	"Mon, 2 Jan 2006 15:04:05 -0700 (MST)",
	"02 Jan 2006 15:04:05 -0700",
}

/*
ParseDateTime takes a date/time string and attempts to parse it and return a newly formatted
date/time that looks like YYYY-MM-DD HH:MM:SS
*/
func ParseDateTime(dateString string) string {
	outputFormat := "2006-01-02 15:04:05"
	var parsedTime time.Time
	var err error

	dateString = strings.TrimSpace(dateString)
	result := ""

	for _, inputFormat := range dateInputFormats {
		if parsedTime, err = time.Parse(inputFormat, dateString); err == nil {
			result = parsedTime.Format(outputFormat)
			break
		}
	}

	if result == "" {
		log.Printf("libmailslurper: ERROR - Parsing date %s", dateString)
		result = dateString
	}

	return result
}
<commit_msg>Now parsing another date format. closes #41<commit_after>// Copyright 2013-2016 Adam Presley. All rights reserved
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package datetime

import (
	"log"
	"strings"
	"time"
)

var dateInputFormats = []string{
	"Mon, 02 Jan 2006 15:04:05 -0700",
	"Mon, 02 Jan 2006 15:04:05 -0700 MST",
	"Mon, 02 Jan 2006 15:04:05 -0700 (MST)",
	"Mon, 2 Jan 2006 15:04:05 -0700 (MST)",
	"02 Jan 2006 15:04:05 -0700",
	"2 Jan 2006 15:04:05 -0700",
}

/*
ParseDateTime takes a date/time string and attempts to parse it and return a newly formatted
date/time that looks like YYYY-MM-DD HH:MM:SS
*/
func ParseDateTime(dateString string) string {
	outputFormat := "2006-01-02 15:04:05"
	var parsedTime time.Time
	var err error

	dateString = strings.TrimSpace(dateString)
	result := ""

	for _, inputFormat := range dateInputFormats {
		if parsedTime, err = time.Parse(inputFormat, dateString); err == nil {
			result = parsedTime.Format(outputFormat)
			break
		}
	}

	if result == "" {
		log.Printf("libmailslurper: ERROR - Parsing date %s", dateString)
		result = dateString
	}

	return result
}
<|endoftext|>


<commit_before>package mails

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"net/url"
	"os"
	"regexp"
	"sync"

	"github.com/toomore/mailbox/campaign"
	"github.com/toomore/mailbox/utils"
)

var (
	htmla    = regexp.MustCompile(`href="(http[s]?://[a-zA-z0-9/\.:?=,-@%()_&\+]+)"`)
	washireg = regexp.MustCompile(`href="({{WASHI}}.+{{\/WASHI}})"`)
)

// ReplaceReader is to replace reader open mail link
func ReplaceReader(html *[]byte, cid string, seed string, uid string) {
	data := url.Values{}
	data.Set("c", cid)
	data.Set("u", uid)
	hm := campaign.MakeMacSeed(seed, data)
	*html = bytes.Replace(
		*html,
		[]byte("{{READER}}"),
		[]byte(fmt.Sprintf("https://%s/read/%x?%s", os.Getenv("mailbox_web_site"), hm, data.Encode())),
		1)
}

// ReplaceFname is to replace FNAME tag
func ReplaceFname(html *[]byte, fname string) {
	*html = bytes.Replace(*html, []byte("{{FNAME}}"), []byte(fname), -1)
}

// ReplaceLname is to replace FNAME tag
func ReplaceLname(html *[]byte, lname string) {
	*html = bytes.Replace(*html, []byte("{{LNAME}}"), []byte(lname), -1)
}

// ReplaceATag is to replace HTML a tag
func ReplaceATag(html *[]byte, allATags map[string]LinksData, cid string, seed string, uid string) {
	data := url.Values{}
	data.Set("c", cid)
	data.Set("u", uid)
	data.Set("t", "a")

	for _, v := range allATags {
		data.Set("l", v.LinkID)
		hm := campaign.MakeMacSeed(seed, data)

		*html = bytes.Replace(*html, []byte(fmt.Sprintf("href=\"%s\"", v.URL)),
			[]byte(fmt.Sprintf("href=\"https://%s/door/%x?%s\"", os.Getenv("mailbox_web_site"), hm, data.Encode())), -1)
	}
}

// ReplaceWashiTag is to replace HTML a tag
func ReplaceWashiTag(html *[]byte, allATags map[string]LinksData, cid string, seed string, uid string) {
	data := url.Values{}
	data.Set("c", cid)
	data.Set("u", uid)

	for _, v := range allATags {
		data.Set("l", v.LinkID)
		hm := campaign.MakeMacSeed(seed, data)

		*html = bytes.Replace(*html, []byte(fmt.Sprintf("href=\"%s\"", v.URL)),
			[]byte(fmt.Sprintf("href=\"https://%s/washi/%x?%s\"", os.Getenv("mailbox_web_site"), hm, data.Encode())), -1)
	}
}

// LinksData is the link data
type LinksData struct {
	Md5h   string
	LinkID string
	URL    []byte
}

// FilterATags is to filter, find all a tag data
func FilterATags(body *[]byte, cid string) map[string]LinksData {
	return filteratags(htmla, body, cid)
}

// FilterWashiTags is to filter, find all {{WASHI}} tag data
func FilterWashiTags(body *[]byte, cid string) map[string]LinksData {
	return filteratags(washireg, body, cid)
}

func filteratags(rg *regexp.Regexp, body *[]byte, cid string) map[string]LinksData {
	allATags := rg.FindAllSubmatch(*body, -1)
	result := make(map[string]LinksData)
	var wg sync.WaitGroup
	wg.Add(len(allATags))
	for _, v := range allATags {
		go func(url []byte) {
			md5h := md5.New()
			md5h.Write(url)
			md5hstr := fmt.Sprintf("%x", md5h.Sum(nil))
			linkID := fmt.Sprintf("%s", utils.GenSeed())
			_, err := utils.GetConn().Query(`INSERT INTO links(id,cid,url,urlhash) VALUES(?,?,?,?)`, linkID, cid, url, md5hstr)
			if err != nil {
				rows, _ := utils.GetConn().Query(`SELECT id FROM links WHERE cid=? AND urlhash=?`, cid, md5hstr)
				for rows.Next() {
					rows.Scan(&linkID)
				}
			}
			result[linkID] = LinksData{
				Md5h:   md5hstr,
				LinkID: linkID,
				URL:    url,
			}
			wg.Done()
		}(v[1])
	}
	wg.Wait()
	return result
}
<commit_msg>Fixed many conn<commit_after>package mails

import (
	"bytes"
	"crypto/md5"
	"fmt"
	"net/url"
	"os"
	"regexp"
	"sync"

	"github.com/toomore/mailbox/campaign"
	"github.com/toomore/mailbox/utils"
)

var (
	htmla    = regexp.MustCompile(`href="(http[s]?://[a-zA-z0-9/\.:?=,-@%()_&\+]+)"`)
	washireg = regexp.MustCompile(`href="({{WASHI}}.+{{\/WASHI}})"`)
)

// ReplaceReader is to replace reader open mail link
func ReplaceReader(html *[]byte, cid string, seed string, uid string) {
	data := url.Values{}
	data.Set("c", cid)
	data.Set("u", uid)
	hm := campaign.MakeMacSeed(seed, data)
	*html = bytes.Replace(
		*html,
		[]byte("{{READER}}"),
		[]byte(fmt.Sprintf("https://%s/read/%x?%s", os.Getenv("mailbox_web_site"), hm, data.Encode())),
		1)
}

// ReplaceFname is to replace FNAME tag
func ReplaceFname(html *[]byte, fname string) {
	*html = bytes.Replace(*html, []byte("{{FNAME}}"), []byte(fname), -1)
}

// ReplaceLname is to replace FNAME tag
func ReplaceLname(html *[]byte, lname string) {
	*html = bytes.Replace(*html, []byte("{{LNAME}}"), []byte(lname), -1)
}

// ReplaceATag is to replace HTML a tag
func ReplaceATag(html *[]byte, allATags map[string]LinksData, cid string, seed string, uid string) {
	data := url.Values{}
	data.Set("c", cid)
	data.Set("u", uid)
	data.Set("t", "a")

	for _, v := range allATags {
		data.Set("l", v.LinkID)
		hm := campaign.MakeMacSeed(seed, data)

		*html = bytes.Replace(*html, []byte(fmt.Sprintf("href=\"%s\"", v.URL)),
			[]byte(fmt.Sprintf("href=\"https://%s/door/%x?%s\"", os.Getenv("mailbox_web_site"), hm, data.Encode())), -1)
	}
}

// ReplaceWashiTag is to replace HTML a tag
func ReplaceWashiTag(html *[]byte, allATags map[string]LinksData, cid string, seed string, uid string) {
	data := url.Values{}
	data.Set("c", cid)
	data.Set("u", uid)

	for _, v := range allATags {
		data.Set("l", v.LinkID)
		hm := campaign.MakeMacSeed(seed, data)

		*html = bytes.Replace(*html, []byte(fmt.Sprintf("href=\"%s\"", v.URL)),
			[]byte(fmt.Sprintf("href=\"https://%s/washi/%x?%s\"", os.Getenv("mailbox_web_site"), hm, data.Encode())), -1)
	}
}

// LinksData is the link data
type LinksData struct {
	Md5h   string
	LinkID string
	URL    []byte
}

// FilterATags is to filter, find all a tag data
func FilterATags(body *[]byte, cid string) map[string]LinksData {
	return filteratags(htmla, body, cid)
}

// FilterWashiTags is to filter, find all {{WASHI}} tag data
func FilterWashiTags(body *[]byte, cid string) map[string]LinksData {
	return filteratags(washireg, body, cid)
}

func filteratags(rg *regexp.Regexp, body *[]byte, cid string) map[string]LinksData {
	var (
		allATags = rg.FindAllSubmatch(*body, -1)
		conn     = utils.GetConn()
		result   = make(map[string]LinksData)
		wg       sync.WaitGroup
	)
	wg.Add(len(allATags))
	for _, v := range allATags {
		go func(url []byte) {
			md5h := md5.New()
			md5h.Write(url)
			md5hstr := fmt.Sprintf("%x", md5h.Sum(nil))
			linkID := fmt.Sprintf("%s", utils.GenSeed())
			_, err := conn.Query(`INSERT INTO links(id,cid,url,urlhash) VALUES(?,?,?,?)`, linkID, cid, url, md5hstr)
			if err != nil {
				rows, _ := conn.Query(`SELECT id FROM links WHERE cid=? AND urlhash=?`, cid, md5hstr)
				for rows.Next() {
					rows.Scan(&linkID)
				}
			}
			result[linkID] = LinksData{
				Md5h:   md5hstr,
				LinkID: linkID,
				URL:    url,
			}
			wg.Done()
		}(v[1])
	}
	wg.Wait()
	conn.Close()
	return result
}
<|endoftext|>


<commit_before>package gps

import (
	"strings"
	"fmt"
	"log"
	"strconv"
	"reflect"
)

// func validateNMEAChecksum determines if a string is a properly formatted NMEA sentence with a valid checksum.
//
// If the input string is valid, output is the input stripped of the "$" token and checksum, along with a boolean 'true'
// If the input string is the incorrect format, the checksum is missing/invalid, or checksum calculation fails, an error string and
// boolean 'false' are returned
//
// Checksum is calculated as XOR of all bytes between "$" and "*"
func validateNMEAChecksum(s string) (string, bool) {
	//validate format. NMEA sentences start with "$" and end in "*xx" where xx is the XOR value of all bytes between
	if !(strings.HasPrefix(s, "$") && strings.Contains(s, "*")) {
		return "Invalid NMEA message", false
	}

	// strip leading "$" and split at "*"
	s_split := strings.Split(strings.TrimPrefix(s, "$"), "*")
	s_out := s_split[0]
	s_cs := s_split[1]

	if len(s_cs) < 2 {
		return "Missing checksum. Fewer than two bytes after asterisk", false
	}

	cs, err := strconv.ParseUint(s_cs[:2], 16, 8)
	if err != nil {
		return "Invalid checksum", false
	}

	cs_calc := byte(0)
	for i := range s_out {
		cs_calc = cs_calc ^ byte(s_out[i])
	}

	if cs_calc != byte(cs) {
		return fmt.Sprintf("Checksum failed. Calculated %#X; expected %#X", cs_calc, cs), false
	}

	return s_out, true
}

func createChecksummedNMEASentence(raw []byte) []byte {
	cs_calc := byte(0)
	for _,v := range raw {
		cs_calc ^= v
	}

	return []byte(fmt.Sprintf("$%s*%02X\r\n", raw, cs_calc))
}

func processNMEASentence(line string, situation *SituationData) {
	sentence, valid := validateNMEAChecksum(line)
	if !valid {
		log.Printf("GPS Error: invalid NMEA string: %s\n", sentence)
		return
	}

	//log.Printf("Begin parse of %s\n", sentence)
	ParseMessage(sentence, situation)
}

type NMEA struct {
	Sentence string
	Tokens []string
	Situation *SituationData
}

// we split the sentence on commas, and use the first field via reflection to find a method with the same name
func ParseMessage(sentence string, situation *SituationData) *NMEA {
	n := &NMEA{ sentence, strings.Split(sentence, ","), situation }

	//log.Printf("NMEA Message type %s, data: %v\n", n.Tokens[0], n.Tokens[1:])

	v := reflect.ValueOf(n)
	m := v.MethodByName(n.Tokens[0])

	if (m == reflect.Value{}) {
		return nil
	}

	m.Call(nil)

	return n
}

func durationSinceMidnight(fixtime string) (int, error) {
	hr, err := strconv.Atoi(fixtime[0:2]); if err != nil { return 0, err }
	min, err := strconv.Atoi(fixtime[2:4]); if err != nil { return 0, err }
	sec, err := strconv.Atoi(fixtime[4:6]); if err != nil { return 0, err }

	return sec + min*60 + hr*60*60, nil
}

func parseLatLon(s string, neg bool) (float32, error) {
	minpos := len(s) - 6
	deg, err := strconv.Atoi(s[0:minpos]); if err != nil { return 0.0, err }
	min, err := strconv.ParseFloat(s[minpos:], 32); if err != nil { return 0.0, err }

	sign := 1; if neg { sign = -1 }

	return float32(sign) * (float32(deg) + float32(min/60.0)), nil
}

func (n *NMEA) GNGGA() { n.GPGGA() } // ublox 8 uses GNGGA in place of GPGGA to indicate multiple nav sources (GPS/GLONASS)
func (n *NMEA) GPGGA() {
	log.Printf("In GPGGA\n")
	s := n.Situation

	s.Mu_GPS.Lock(); defer s.Mu_GPS.Unlock()

	d, err := durationSinceMidnight(n.Tokens[1]); if err != nil { return }
	s.LastFixSinceMidnightUTC = uint32(d)

	if len(n.Tokens[2]) < 4 || len(n.Tokens[4]) < 4 { return } // sanity check lat/lon

	lat, err := parseLatLon(n.Tokens[2], n.Tokens[3] == "S"); if err != nil { return }
	lon, err := parseLatLon(n.Tokens[4], n.Tokens[5] == "W"); if err != nil { return }

	s.Lat = lat; s.Lng = lon

	log.Printf("Situation: %v\n", s)
}

func (n *NMEA) GPGSA() {
	log.Printf("In GPGSA\n")

}
<commit_msg>situation constructor (mutexes weren't getting init'd)<commit_after>package gps

import (
	"strings"
	"fmt"
	"log"
	"strconv"
	"reflect"
)

// func validateNMEAChecksum determines if a string is a properly formatted NMEA sentence with a valid checksum.
//
// If the input string is valid, output is the input stripped of the "$" token and checksum, along with a boolean 'true'
// If the input string is the incorrect format, the checksum is missing/invalid, or checksum calculation fails, an error string and
// boolean 'false' are returned
//
// Checksum is calculated as XOR of all bytes between "$" and "*"
func validateNMEAChecksum(s string) (string, bool) {
	//validate format. NMEA sentences start with "$" and end in "*xx" where xx is the XOR value of all bytes between
	if !(strings.HasPrefix(s, "$") && strings.Contains(s, "*")) {
		return "Invalid NMEA message", false
	}

	// strip leading "$" and split at "*"
	s_split := strings.Split(strings.TrimPrefix(s, "$"), "*")
	s_out := s_split[0]
	s_cs := s_split[1]

	if len(s_cs) < 2 {
		return "Missing checksum. Fewer than two bytes after asterisk", false
	}

	cs, err := strconv.ParseUint(s_cs[:2], 16, 8)
	if err != nil {
		return "Invalid checksum", false
	}

	cs_calc := byte(0)
	for i := range s_out {
		cs_calc = cs_calc ^ byte(s_out[i])
	}

	if cs_calc != byte(cs) {
		return fmt.Sprintf("Checksum failed. Calculated %#X; expected %#X", cs_calc, cs), false
	}

	return s_out, true
}

func createChecksummedNMEASentence(raw []byte) []byte {
	cs_calc := byte(0)
	for _,v := range raw {
		cs_calc ^= v
	}

	return []byte(fmt.Sprintf("$%s*%02X\r\n", raw, cs_calc))
}

func processNMEASentence(line string, situation *SituationData) {
	sentence, valid := validateNMEAChecksum(line)
	if !valid {
		log.Printf("GPS Error: invalid NMEA string: %s\n", sentence)
		return
	}

	//log.Printf("Begin parse of %s\n", sentence)
	ParseMessage(sentence, situation)
}

type NMEA struct {
	Sentence string
	Tokens []string
	Situation *SituationData
}

// we split the sentence on commas, and use the first field via reflection to find a method with the same name
func ParseMessage(sentence string, situation *SituationData) *NMEA {
	n := &NMEA{ sentence, strings.Split(sentence, ","), situation }

	//log.Printf("NMEA Message type %s, data: %v\n", n.Tokens[0], n.Tokens[1:])

	v := reflect.ValueOf(n)
	m := v.MethodByName(n.Tokens[0])

	if (m == reflect.Value{}) {
		return nil
	}

	m.Call(nil)

	return n
}

func durationSinceMidnight(fixtime string) (int, error) {
	hr, err := strconv.Atoi(fixtime[0:2]); if err != nil { return 0, err }
	min, err := strconv.Atoi(fixtime[2:4]); if err != nil { return 0, err }
	sec, err := strconv.Atoi(fixtime[4:6]); if err != nil { return 0, err }

	return sec + min*60 + hr*60*60, nil
}

func parseLatLon(s string, neg bool) (float32, error) {
	minpos := len(s) - 5
	deg, err := strconv.Atoi(s[0:minpos]); if err != nil { return 0.0, err }
	min, err := strconv.ParseFloat(s[minpos:], 32); if err != nil { return 0.0, err }

	sign := 1; if neg { sign = -1 }

	return float32(sign) * (float32(deg) + float32(min/60.0)), nil
}

func (n *NMEA) GNGGA() { n.GPGGA() } // ublox 8 uses GNGGA in place of GPGGA to indicate multiple nav sources (GPS/GLONASS)
func (n *NMEA) GPGGA() {
	log.Printf("In GPGGA\n")
	s := n.Situation

	s.Mu_GPS.Lock(); defer s.Mu_GPS.Unlock()

	d, err := durationSinceMidnight(n.Tokens[1]); if err != nil { return }
	s.LastFixSinceMidnightUTC = uint32(d)

	if len(n.Tokens[2]) < 4 || len(n.Tokens[4]) < 4 { return } // sanity check lat/lon

	lat, err := parseLatLon(n.Tokens[2], n.Tokens[3] == "S"); if err != nil { return }
	lon, err := parseLatLon(n.Tokens[4], n.Tokens[5] == "W"); if err != nil { return }

	s.Lat = lat; s.Lng = lon

	log.Printf("Situation: %v\n", s)
}

func (n *NMEA) GPGSA() {
	log.Printf("In GPGSA\n")

}
<|endoftext|>


<commit_before>package goevent_test

import (
	"sync"
	"testing"

	"github.com/pocke/goevent"
)

func TestEventNew(t *testing.T) {
	p := goevent.New()
	t.Log("Event: %+v", p)
}

func TestOnTrigger(t *testing.T) {
	p := goevent.New()

	i := 1
	err := p.On(func(j int) {
		i += j
	})
	if err != nil {
		t.Fatal(err)
	}

	err = p.Trigger(2)
	if i != 3 {
		t.Errorf("Expected i == 3, Got i == %d", i)
	}
	if err != nil {
		t.Error("should not return error When not reject. But got %s.", err)
	}

	err = p.Trigger("2")
	if err == nil {
		t.Error("should return error when invalid type. But got nil")
	}
}

func TestManyTrigger(t *testing.T) {
	p := goevent.New()
	i := 0
	p.On(func(j int) {
		i += j
	})

	for j := 0; j < 1000; j++ {
		p.Trigger(1)
	}

	if i != 1000 {
		t.Errorf("i should be 1000, but got %d", i)
	}
}

func TestManyOn(t *testing.T) {
	p := goevent.New()
	i := 0
	m := sync.Mutex{}
	for j := 0; j < 1000; j++ {
		p.On(func(j int) {
			m.Lock()
			defer m.Unlock()
			i += j
		})
	}
	p.Trigger(1)
	if i != 1000 {
		t.Errorf("i should be 1000, but got %d", i)
	}
}

func TestOnWhenNotFunction(t *testing.T) {
	p := goevent.New()
	err := p.On("foobar")
	if err == nil {
		t.Error("should return error When recieve not function. But got nil.")
	}
}

func TestOnWhenInvalidArgs(t *testing.T) {
	p := goevent.New()
	p.On(func(i int) {})

	err := p.On(func() {})
	if err == nil {
		t.Error("Should return error when different argument num. But got nil")
	}

	err = p.On(func(s string) {})
	if err == nil {
		t.Error("Should return error when different args type. But got nil")
	}
}

func TestOff(t *testing.T) {
	p := goevent.New()
	i := 0
	j := 0
	k := 0

	p.On(func() { j++ })
	f := func() { i++ }
	p.On(f)
	p.On(func() { k++ })

	p.Trigger()
	if i != 1 {
		t.Errorf("i expected 1, but got %d", i)
	}
	if j != 1 {
		t.Errorf("j expected 1, but got %d", j)
	}
	if k != 1 {
		t.Errorf("k expected 1, but got %d", k)
	}

	err := p.Off(f)
	if err != nil {
		t.Error(err)
	}
	p.Trigger()
	if i != 1 {
		t.Errorf("i expected 1, but got %d", i)
	}
	if j != 2 {
		t.Errorf("j expected 2, but got %d", j)
	}
	if k != 2 {
		t.Errorf("k expected 2, but got %d", k)
	}

	err = p.Off(f)
	if err == nil {
		t.Errorf("should return error when Listener doesn't exists. but got nil")
	}
}
<commit_msg>add test<commit_after>package goevent_test

import (
	"sync"
	"testing"

	"github.com/pocke/goevent"
)

func TestEventNew(t *testing.T) {
	p := goevent.New()
	t.Log("Event: %+v", p)
}

func TestOnTrigger(t *testing.T) {
	p := goevent.New()

	i := 1
	err := p.On(func(j int) {
		i += j
	})
	if err != nil {
		t.Fatal(err)
	}

	err = p.Trigger(2)
	if i != 3 {
		t.Errorf("Expected i == 3, Got i == %d", i)
	}
	if err != nil {
		t.Error("should not return error When not reject. But got %s.", err)
	}

	err = p.Trigger("2")
	if err == nil {
		t.Error("should return error when invalid type. But got nil")
	}
}

func TestManyTrigger(t *testing.T) {
	p := goevent.New()
	i := 0
	p.On(func(j int) {
		i += j
	})

	for j := 0; j < 1000; j++ {
		p.Trigger(1)
	}

	if i != 1000 {
		t.Errorf("i should be 1000, but got %d", i)
	}
}

func TestManyOn(t *testing.T) {
	p := goevent.New()
	i := 0
	m := sync.Mutex{}
	for j := 0; j < 1000; j++ {
		p.On(func(j int) {
			m.Lock()
			defer m.Unlock()
			i += j
		})
	}
	p.Trigger(1)
	if i != 1000 {
		t.Errorf("i should be 1000, but got %d", i)
	}
}

func TestManyArgs(t *testing.T) {
	e := goevent.New()
	var res []int
	e.On(func(i, j, k int) {
		res = append(res, i)
		res = append(res, j)
		res = append(res, k)
	})
	e.Trigger(1, 2, 3)
	if !(res[0] == 1 && res[1] == 2 && res[2] == 3) {
		t.Errorf("res expected %v, but got %v", []int{1, 2, 3}, res)
	}
}

func TestOnWhenNotFunction(t *testing.T) {
	p := goevent.New()
	err := p.On("foobar")
	if err == nil {
		t.Error("should return error When recieve not function. But got nil.")
	}
}

func TestOnWhenInvalidArgs(t *testing.T) {
	p := goevent.New()
	p.On(func(i int) {})

	err := p.On(func() {})
	if err == nil {
		t.Error("Should return error when different argument num. But got nil")
	}

	err = p.On(func(s string) {})
	if err == nil {
		t.Error("Should return error when different args type. But got nil")
	}
}

func TestOff(t *testing.T) {
	p := goevent.New()
	i := 0
	j := 0
	k := 0

	p.On(func() { j++ })
	f := func() { i++ }
	p.On(f)
	p.On(func() { k++ })

	p.Trigger()
	if i != 1 {
		t.Errorf("i expected 1, but got %d", i)
	}
	if j != 1 {
		t.Errorf("j expected 1, but got %d", j)
	}
	if k != 1 {
		t.Errorf("k expected 1, but got %d", k)
	}

	err := p.Off(f)
	if err != nil {
		t.Error(err)
	}
	p.Trigger()
	if i != 1 {
		t.Errorf("i expected 1, but got %d", i)
	}
	if j != 2 {
		t.Errorf("j expected 2, but got %d", j)
	}
	if k != 2 {
		t.Errorf("k expected 2, but got %d", k)
	}

	err = p.Off(f)
	if err == nil {
		t.Errorf("should return error when Listener doesn't exists. but got nil")
	}
}
<|endoftext|>


<commit_before>package gostats

import(
  "testing"
  "os"
  "time"
  "github.com/stretchr/testify/assert"
)

type metricNameTest struct{
  Source    string
  Expected  string
}
func TestSanitizeMetricName(t *testing.T){
  cases := []metricNameTest{
    metricNameTest{"mymetricname", "mymetricname"},
    metricNameTest{"my metric name", "my_metric_name"},
    metricNameTest{"my/metric/name", "my_metric_name"},
    metricNameTest{"my.metric name", "my_metric_name"},
    metricNameTest{"my-metric/name", "my-metric_name"},
    metricNameTest{"my-metric@name", "my-metricname"},
  }

  for _,c := range cases{
    n := sanitizeMetricName(c.Source)
    if n != c.Expected{
      assert.Equal(t, c.Expected, n, "metric name should be sanitized correctly")
    }
  }
}

func TestNew(t *testing.T){
  s := New()
  h, _ := os.Hostname()
  assert.Equal(t, sanitizeMetricName(h), s.Hostname, "hostname should be set")
  assert.Equal(t, "gostats", s.ClientName, "default client name should be set")

  s.Hostname = "localhost"
  assert.Equal(t, "gostats.localhost", s.MetricBase(), "metric base should be correct")
}

func TestStart(t *testing.T){
  s, err := Start("localhost:8015", 5, "testclient")
  defer s.Stop()

  assert.Nil(t, err)

  s.Hostname = "localhost"
  assert.Equal(t, "testclient.localhost", s.MetricBase(), "metric base should be correct")
  assert.Equal(t, time.Duration(5*time.Second), s.PushInterval, "push interval should be correct")
  assert.Equal(t, "localhost:8015", s.StatsdHost, "statsd host should be correct")
}
<commit_msg>Fix tests<commit_after>package gostats

import(
  "testing"
  "os"
  "time"
  "github.com/stretchr/testify/assert"
)

type metricNameTest struct{
  Source    string
  Expected  string
}
func TestSanitizeMetricName(t *testing.T){
  cases := []metricNameTest{
    metricNameTest{"mymetricname", "mymetricname"},
    metricNameTest{"my metric name", "my_metric_name"},
    metricNameTest{"my/metric/name", "my_metric_name"},
    metricNameTest{"my.metric name", "my_metric_name"},
    metricNameTest{"my-metric/name", "my-metric_name"},
    metricNameTest{"my-metric@name", "my-metricname"},
  }

  for _,c := range cases{
    n := sanitizeMetricName(c.Source)
    if n != c.Expected{
      assert.Equal(t, c.Expected, n, "metric name should be sanitized correctly")
    }
  }
}

func TestNew(t *testing.T){
  s := New()
  h, _ := os.Hostname()
  assert.Equal(t, sanitizeMetricName(h), s.Hostname, "hostname should be set")
  assert.Equal(t, "gostats", s.ClientName, "default client name should be set")

  s.Hostname = "localhost"
  assert.Equal(t, "gostats.gostats.localhost.", s.MetricBase(), "metric base should be correct")
}

func TestStart(t *testing.T){
  s, err := Start("localhost:8015", 5, "testclient")
  defer s.Stop()

  assert.Nil(t, err)

  s.Hostname = "localhost"
  assert.Equal(t, "gostats.testclient.localhost.", s.MetricBase(), "metric base should be correct")
  assert.Equal(t, time.Duration(5*time.Second), s.PushInterval, "push interval should be correct")
  assert.Equal(t, "localhost:8015", s.StatsdHost, "statsd host should be correct")
}
<|endoftext|>


<commit_before>package graph

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"sourcegraph.com/sourcegraph/srcgraph/repo"
)

type (
	SID        int64
	SymbolPath string
	SymbolKind string
)

// SymbolKey specifies a symbol, either concretely or abstractly. A concrete
// symbol key has a non-empty CommitID and refers to a symbol defined in a
// specific commit. An abstract symbol key has an empty CommitID and is
// considered to refer to symbols from any number of commits (so long as the
// Repo, UnitType, Unit, and Path match).
//
// You can think of CommitID as the time dimension. With an empty CommitID, you
// are referring to a symbol that may or may not exist at various times. With a
// non-empty CommitID, you are referring to a specific definition of a symbol at
// the time specified by the CommitID.
type SymbolKey struct {
	// Repo is the VCS repository that defines this symbol. Its Elasticsearch mapping is defined
	// separately.
	Repo repo.URI `json:",omitempty"`

	// CommitID is the ID of the VCS commit that this symbol was defined in. The
	// CommitID is always a full commit ID (40 hexadecimal characters for git
	// and hg), never a branch or tag name.
	CommitID string `db:"commit_id" json:",omitempty"`

	// UnitType is the type name of the source unit (obtained from unit.Type(u))
	// that this symbol was defined in.
	UnitType string `db:"unit_type" json:",omitempty"`

	// Unit is the name of the source unit (obtained from u.Name()) that this
	// symbol was defined in.
	Unit string `json:",omitempty"`

	// Path is the path to this symbol, relative to the repo. Its Elasticsearch mapping is defined
	// separately (because it is a multi_field, which the struct tag can't currently represent).
	Path SymbolPath
}

func (s SymbolKey) String() string {
	b, err := json.Marshal(s)
	if err != nil {
		panic("SymbolKey.String: " + err.Error())
	}
	return string(b)
}

type Symbol struct {
	// SID is a unique, sequential ID for a symbol. It is regenerated each time
	// the symbol is emitted by the grapher and saved to the database. The SID
	// is used as an optimization (e.g., joins are faster on SID than on
	// SymbolKey).
	SID SID `db:"sid" json:",omitempty" elastic:"type:integer,index:no"`

	// SymbolKey is the natural unique key for a symbol. It is stable
	// (subsequent runs of a grapher will emit the same symbols with the same
	// SymbolKeys).
	SymbolKey

	// SpecificPath is the language-specific "path" to this symbol, using
	// language-specific separators (e.g., "::" and "." instead of "/", which is
	// used in the SymbolKey.Path value).
	SpecificPath string `db:"specific_path"`

	// Kind is the language-independent kind of this symbol.
	Kind SymbolKind `elastic:"type:string,index:analyzed"`

	// SpecificKind is the language-specific kind of this symbol (which is in
	// some cases equal to the Kind).
	SpecificKind string `db:"specific_kind"`

	Name string

	// Callable is true if this symbol may be called or invoked, such as in the
	// case of functions or methods.
	Callable bool `db:"callable"`

	File string `elastic:"type:string,index:no"`

	DefStart int `db:"def_start" elastic:"type:integer,index:no"`
	DefEnd   int `db:"def_end" elastic:"type:integer,index:no"`

	Exported bool `elastic:"type:boolean,index:not_analyzed"`

	// Test is whether this symbol is defined in test code (as opposed to main
	// code). For example, definitions in Go *_test.go files have Test = true.
	Test bool `elastic:"type:boolean,index:not_analyzed"`

	TypeExpr string `db:"type_expr" json:",omitempty"`
}

func (s *Symbol) Language() string {
	switch s.UnitType {
	case "GoPackage":
		return "Go"
		// TODO(sqs): add Python, JS, etc.
	}
	return "unknown language"
}

// TODO!(sqs): factor this into the individual source unit packages
func (s *Symbol) Signature() string {
	var removeOwnImportPath = func(str string) string {
		if s.UnitType == "GoPackage" {
			return strings.Replace(strings.Replace(str, string(s.Repo)+".", "", -1), string(s.Repo)+"/", "", -1)
		}
		return str
	}

	if s.TypeExpr == "" {
		return ""
	}
	if !s.Callable {
		if (s.Kind == Field || s.Kind == Var || s.Kind == Type) && len(s.TypeExpr) < 50 {
			return " " + removeOwnImportPath(s.TypeExpr)
		}
		return ""
	}

	switch s.UnitType {
	case "GoPackage":
		return removeOwnImportPath(strings.TrimPrefix(s.TypeExpr, "func"))
	case "js":
		return strings.TrimPrefix(s.TypeExpr, "fn")
	case "python":
		// remove up to first paren
		i := strings.Index(s.TypeExpr, "(")
		if i == -1 {
			return s.TypeExpr
		}
		return s.TypeExpr[:i]
	case "ruby":
		return s.TypeExpr
	}
	return s.TypeExpr
}

func (s *Symbol) sortKey() string { return s.SymbolKey.String() }

// Propagate describes type/value propagation in code. A Propagate entry from A
// (src) to B (dst) indicates that the type/value of A propagates to B. In Tern,
// this is indicated by A having a "fwd" property whose value is an array that
// includes B.
//
//
// ## Motivation & example
//
// For example, consider the following JavaScript code:
//
//   var a = Foo;
//   var b = a;
//
// Foo, a, and b are each their own symbol. We could resolve all of them to the
// symbol of their original type (perhaps Foo), but there are occasions when you
// do want to see only the definition of a or b and examples thereof. Therefore,
// we need to represent them as distinct symbols.
//
// Even though Foo, a, and b are distinct symbols, there are propagation
// relationships between them that are important to represent. The type of Foo
// propagates to both a and b, and the type of a propagates to b. In this case,
// we would have 3 Propagates: Propagate{Src: "Foo", Dst: "a"}, Propagate{Src:
// "Foo", Dst: "b"}, and Propagate{Src: "a", Dst: "b"}. (The propagation
// relationships could be described by just the first and last Propagates, but
// we explicitly include all paths as a denormalization optimization to avoid
// requiring an unbounded number of DB queries to determine which symbols a type
// propagates to or from.)
//
//
// ## Directionality
//
// Propagation is unidirectional, in the general case. In the example above, if
// Foo referred to a JavaScript object and if the code were evaluated, any
// *runtime* type changes (e.g., setting a property) on Foo, a, and b would be
// reflected on all of the others. But this doesn't hold for static analysis;
// it's not always true that if a property "a.x" or "b.x" exists, then "Foo.x"
// exists. The simplest example is when Foo is an external definition. Perhaps
// this example file (which uses Foo as a library) modifies Foo to add a new
// property, but other libraries that use Foo would never see that property
// because they wouldn't be executed in the same context as this example file.
// So, in general, we cannot say that Foo receives all types applied to symbols
// that Foo propagates to.
//
//
// ## Hypothetical Python example
//
// Consider the following 2 Python files:
//
//   """file1.py"""
//   class Foo(object): end
//
//   """file2.py"""
//   from .file1 import Foo
//   Foo2 = Foo
//
// In this example, there would be one Propagate: Propagate{Src: "file1/Foo",
// Dst: "file2/Foo2}.
type Propagate struct {
	// Src is the symbol whose type/value is being propagated to the dst symbol.
	SrcRepo     repo.URI
	SrcPath     SymbolPath
	SrcUnit     string
	SrcUnitType string

	// Dst is the symbol that is receiving a propagated type/value from the src symbol.
	DstRepo     repo.URI
	DstPath     SymbolPath
	DstUnit     string
	DstUnitType string
}

const (
	Const   SymbolKind = "const"
	Field              = "field"
	Func               = "func"
	Module             = "module"
	Package            = "package"
	Type               = "type"
	Var                = "var"
)

var AllSymbolKinds = []SymbolKind{Const, Field, Func, Module, Package, Type, Var}

// Returns true iff k is a known symbol kind.
func (k SymbolKind) Valid() bool {
	for _, kk := range AllSymbolKinds {
		if k == kk {
			return true
		}
	}
	return false
}

func KindName(k string) string {
	if strings.HasSuffix(k, "_module") {
		return "module"
	}
	return strings.Replace(k, "_", " ", -1)
}

type ConstData struct {
	ConstValue string
}

// SQL

func (x SID) Value() (driver.Value, error) {
	return int64(x), nil
}

func (x *SID) Scan(v interface{}) error {
	if data, ok := v.(int64); ok {
		*x = SID(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolPath) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolPath) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolPath(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolKind) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolKind) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolKind(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x *ConstData) Value() (driver.Value, error) {
	if x == nil {
		return nil, nil
	}
	return json.Marshal(x)
}

func (x *ConstData) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		return json.Unmarshal(data, x)
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

// Debugging

func (x Symbol) String() string {
	s, _ := json.Marshal(x)
	return string(s)
}

// Sorting

type Symbols []*Symbol

func (vs Symbols) Len() int           { return len(vs) }
func (vs Symbols) Swap(i, j int)      { vs[i], vs[j] = vs[j], vs[i] }
func (vs Symbols) Less(i, j int) bool { return vs[i].sortKey() < vs[j].sortKey() }

func (syms Symbols) Keys() (keys []SymbolKey) {
	keys = make([]SymbolKey, len(syms))
	for i, sym := range syms {
		keys[i] = sym.SymbolKey
	}
	return
}

func (syms Symbols) SIDs() (ids []SID) {
	ids = make([]SID, len(syms))
	for i, sym := range syms {
		ids[i] = sym.SID
	}
	return
}

func ParseSIDs(sidstrs []string) (sids []SID) {
	sids = make([]SID, len(sidstrs))
	for i, sidstr := range sidstrs {
		sid, err := strconv.Atoi(sidstr)
		if err == nil {
			sids[i] = SID(sid)
		}
	}
	return
}
<commit_msg>Test field omitempty (json tag)<commit_after>package graph

import (
	"database/sql/driver"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"sourcegraph.com/sourcegraph/srcgraph/repo"
)

type (
	SID        int64
	SymbolPath string
	SymbolKind string
)

// SymbolKey specifies a symbol, either concretely or abstractly. A concrete
// symbol key has a non-empty CommitID and refers to a symbol defined in a
// specific commit. An abstract symbol key has an empty CommitID and is
// considered to refer to symbols from any number of commits (so long as the
// Repo, UnitType, Unit, and Path match).
//
// You can think of CommitID as the time dimension. With an empty CommitID, you
// are referring to a symbol that may or may not exist at various times. With a
// non-empty CommitID, you are referring to a specific definition of a symbol at
// the time specified by the CommitID.
type SymbolKey struct {
	// Repo is the VCS repository that defines this symbol. Its Elasticsearch mapping is defined
	// separately.
	Repo repo.URI `json:",omitempty"`

	// CommitID is the ID of the VCS commit that this symbol was defined in. The
	// CommitID is always a full commit ID (40 hexadecimal characters for git
	// and hg), never a branch or tag name.
	CommitID string `db:"commit_id" json:",omitempty"`

	// UnitType is the type name of the source unit (obtained from unit.Type(u))
	// that this symbol was defined in.
	UnitType string `db:"unit_type" json:",omitempty"`

	// Unit is the name of the source unit (obtained from u.Name()) that this
	// symbol was defined in.
	Unit string `json:",omitempty"`

	// Path is the path to this symbol, relative to the repo. Its Elasticsearch mapping is defined
	// separately (because it is a multi_field, which the struct tag can't currently represent).
	Path SymbolPath
}

func (s SymbolKey) String() string {
	b, err := json.Marshal(s)
	if err != nil {
		panic("SymbolKey.String: " + err.Error())
	}
	return string(b)
}

type Symbol struct {
	// SID is a unique, sequential ID for a symbol. It is regenerated each time
	// the symbol is emitted by the grapher and saved to the database. The SID
	// is used as an optimization (e.g., joins are faster on SID than on
	// SymbolKey).
	SID SID `db:"sid" json:",omitempty" elastic:"type:integer,index:no"`

	// SymbolKey is the natural unique key for a symbol. It is stable
	// (subsequent runs of a grapher will emit the same symbols with the same
	// SymbolKeys).
	SymbolKey

	// SpecificPath is the language-specific "path" to this symbol, using
	// language-specific separators (e.g., "::" and "." instead of "/", which is
	// used in the SymbolKey.Path value).
	SpecificPath string `db:"specific_path"`

	// Kind is the language-independent kind of this symbol.
	Kind SymbolKind `elastic:"type:string,index:analyzed"`

	// SpecificKind is the language-specific kind of this symbol (which is in
	// some cases equal to the Kind).
	SpecificKind string `db:"specific_kind"`

	Name string

	// Callable is true if this symbol may be called or invoked, such as in the
	// case of functions or methods.
	Callable bool `db:"callable"`

	File string `elastic:"type:string,index:no"`

	DefStart int `db:"def_start" elastic:"type:integer,index:no"`
	DefEnd   int `db:"def_end" elastic:"type:integer,index:no"`

	Exported bool `elastic:"type:boolean,index:not_analyzed"`

	// Test is whether this symbol is defined in test code (as opposed to main
	// code). For example, definitions in Go *_test.go files have Test = true.
	Test bool `elastic:"type:boolean,index:not_analyzed" json:",omitempty"`

	TypeExpr string `db:"type_expr" json:",omitempty"`
}

func (s *Symbol) Language() string {
	switch s.UnitType {
	case "GoPackage":
		return "Go"
		// TODO(sqs): add Python, JS, etc.
	}
	return "unknown language"
}

// TODO!(sqs): factor this into the individual source unit packages
func (s *Symbol) Signature() string {
	var removeOwnImportPath = func(str string) string {
		if s.UnitType == "GoPackage" {
			return strings.Replace(strings.Replace(str, string(s.Repo)+".", "", -1), string(s.Repo)+"/", "", -1)
		}
		return str
	}

	if s.TypeExpr == "" {
		return ""
	}
	if !s.Callable {
		if (s.Kind == Field || s.Kind == Var || s.Kind == Type) && len(s.TypeExpr) < 50 {
			return " " + removeOwnImportPath(s.TypeExpr)
		}
		return ""
	}

	switch s.UnitType {
	case "GoPackage":
		return removeOwnImportPath(strings.TrimPrefix(s.TypeExpr, "func"))
	case "js":
		return strings.TrimPrefix(s.TypeExpr, "fn")
	case "python":
		// remove up to first paren
		i := strings.Index(s.TypeExpr, "(")
		if i == -1 {
			return s.TypeExpr
		}
		return s.TypeExpr[:i]
	case "ruby":
		return s.TypeExpr
	}
	return s.TypeExpr
}

func (s *Symbol) sortKey() string { return s.SymbolKey.String() }

// Propagate describes type/value propagation in code. A Propagate entry from A
// (src) to B (dst) indicates that the type/value of A propagates to B. In Tern,
// this is indicated by A having a "fwd" property whose value is an array that
// includes B.
//
//
// ## Motivation & example
//
// For example, consider the following JavaScript code:
//
//   var a = Foo;
//   var b = a;
//
// Foo, a, and b are each their own symbol. We could resolve all of them to the
// symbol of their original type (perhaps Foo), but there are occasions when you
// do want to see only the definition of a or b and examples thereof. Therefore,
// we need to represent them as distinct symbols.
//
// Even though Foo, a, and b are distinct symbols, there are propagation
// relationships between them that are important to represent. The type of Foo
// propagates to both a and b, and the type of a propagates to b. In this case,
// we would have 3 Propagates: Propagate{Src: "Foo", Dst: "a"}, Propagate{Src:
// "Foo", Dst: "b"}, and Propagate{Src: "a", Dst: "b"}. (The propagation
// relationships could be described by just the first and last Propagates, but
// we explicitly include all paths as a denormalization optimization to avoid
// requiring an unbounded number of DB queries to determine which symbols a type
// propagates to or from.)
//
//
// ## Directionality
//
// Propagation is unidirectional, in the general case. In the example above, if
// Foo referred to a JavaScript object and if the code were evaluated, any
// *runtime* type changes (e.g., setting a property) on Foo, a, and b would be
// reflected on all of the others. But this doesn't hold for static analysis;
// it's not always true that if a property "a.x" or "b.x" exists, then "Foo.x"
// exists. The simplest example is when Foo is an external definition. Perhaps
// this example file (which uses Foo as a library) modifies Foo to add a new
// property, but other libraries that use Foo would never see that property
// because they wouldn't be executed in the same context as this example file.
// So, in general, we cannot say that Foo receives all types applied to symbols
// that Foo propagates to.
//
//
// ## Hypothetical Python example
//
// Consider the following 2 Python files:
//
//   """file1.py"""
//   class Foo(object): end
//
//   """file2.py"""
//   from .file1 import Foo
//   Foo2 = Foo
//
// In this example, there would be one Propagate: Propagate{Src: "file1/Foo",
// Dst: "file2/Foo2}.
type Propagate struct {
	// Src is the symbol whose type/value is being propagated to the dst symbol.
	SrcRepo     repo.URI
	SrcPath     SymbolPath
	SrcUnit     string
	SrcUnitType string

	// Dst is the symbol that is receiving a propagated type/value from the src symbol.
	DstRepo     repo.URI
	DstPath     SymbolPath
	DstUnit     string
	DstUnitType string
}

const (
	Const   SymbolKind = "const"
	Field              = "field"
	Func               = "func"
	Module             = "module"
	Package            = "package"
	Type               = "type"
	Var                = "var"
)

var AllSymbolKinds = []SymbolKind{Const, Field, Func, Module, Package, Type, Var}

// Returns true iff k is a known symbol kind.
func (k SymbolKind) Valid() bool {
	for _, kk := range AllSymbolKinds {
		if k == kk {
			return true
		}
	}
	return false
}

func KindName(k string) string {
	if strings.HasSuffix(k, "_module") {
		return "module"
	}
	return strings.Replace(k, "_", " ", -1)
}

type ConstData struct {
	ConstValue string
}

// SQL

func (x SID) Value() (driver.Value, error) {
	return int64(x), nil
}

func (x *SID) Scan(v interface{}) error {
	if data, ok := v.(int64); ok {
		*x = SID(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolPath) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolPath) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolPath(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x SymbolKind) Value() (driver.Value, error) {
	return string(x), nil
}

func (x *SymbolKind) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		*x = SymbolKind(data)
		return nil
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

func (x *ConstData) Value() (driver.Value, error) {
	if x == nil {
		return nil, nil
	}
	return json.Marshal(x)
}

func (x *ConstData) Scan(v interface{}) error {
	if data, ok := v.([]byte); ok {
		return json.Unmarshal(data, x)
	}
	return fmt.Errorf("%T.Scan failed: %v", x, v)
}

// Debugging

func (x Symbol) String() string {
	s, _ := json.Marshal(x)
	return string(s)
}

// Sorting

type Symbols []*Symbol

func (vs Symbols) Len() int           { return len(vs) }
func (vs Symbols) Swap(i, j int)      { vs[i], vs[j] = vs[j], vs[i] }
func (vs Symbols) Less(i, j int) bool { return vs[i].sortKey() < vs[j].sortKey() }

func (syms Symbols) Keys() (keys []SymbolKey) {
	keys = make([]SymbolKey, len(syms))
	for i, sym := range syms {
		keys[i] = sym.SymbolKey
	}
	return
}

func (syms Symbols) SIDs() (ids []SID) {
	ids = make([]SID, len(syms))
	for i, sym := range syms {
		ids[i] = sym.SID
	}
	return
}

func ParseSIDs(sidstrs []string) (sids []SID) {
	sids = make([]SID, len(sidstrs))
	for i, sidstr := range sidstrs {
		sid, err := strconv.Atoi(sidstr)
		if err == nil {
			sids[i] = SID(sid)
		}
	}
	return
}
<|endoftext|>


<commit_before>package graph

import (
	"encoding/hex"
	"fmt"

	"github.com/cayleygraph/quad"
)

// Ref defines an opaque "quad store reference" type. However the backend wishes
// to implement it, a Ref is merely a token to a quad or a node that the
// backing store itself understands, and the base iterators pass around.
//
// For example, in a very traditional, graphd-style graph, these are int64s
// (guids of the primitives). In a very direct sort of graph, these could be
// pointers to structs, or merely quads, or whatever works best for the
// backing store.
//
// These must be comparable, or return a comparable version on Key.
type Ref interface {
	// Key returns a dynamic type that is comparable according to the Go language specification.
	// The returned value must be unique for each receiver value.
	Key() interface{}
}

func HashOf(s quad.Value) (out ValueHash) {
	if s == nil {
		return
	}
	quad.HashTo(s, out[:])
	return
}

var _ Ref = ValueHash{}

// ValueHash is a hash of a single value.
type ValueHash [quad.HashSize]byte

func (h ValueHash) Valid() bool {
	return h != ValueHash{}
}
func (h ValueHash) Key() interface{} { return h }
func (h ValueHash) String() string {
	if !h.Valid() {
		return ""
	}
	return hex.EncodeToString(h[:])
}

// PreFetchedValue is an optional interface for graph.Ref to indicate that
// quadstore has already loaded a value into memory.
type PreFetchedValue interface {
	Ref
	NameOf() quad.Value
}

func PreFetched(v quad.Value) PreFetchedValue {
	return fetchedValue{v}
}

type fetchedValue struct {
	Val quad.Value
}

func (v fetchedValue) IsNode() bool       { return true }
func (v fetchedValue) NameOf() quad.Value { return v.Val }
func (v fetchedValue) Key() interface{}   { return v.Val }

// Keyer provides a method for comparing types that are not otherwise comparable.
// The Key method must return a dynamic type that is comparable according to the
// Go language specification. The returned value must be unique for each receiver
// value.
//
// Deprecated: Ref contains the same method now.
type Keyer = Ref

// ToKey prepares Ref to be stored inside maps, calling Key() if necessary.
func ToKey(v Ref) interface{} {
	if v == nil {
		return nil
	}
	return v.Key()
}

var _ Ref = QuadHash{}

type QuadHash struct {
	Subject   ValueHash
	Predicate ValueHash
	Object    ValueHash
	Label     ValueHash
}

func (q QuadHash) Dirs() [4]ValueHash {
	return [4]ValueHash{
		q.Subject,
		q.Predicate,
		q.Object,
		q.Label,
	}
}
func (q QuadHash) Key() interface{} { return q }
func (q QuadHash) Get(d quad.Direction) ValueHash {
	switch d {
	case quad.Subject:
		return q.Subject
	case quad.Predicate:
		return q.Predicate
	case quad.Object:
		return q.Object
	case quad.Label:
		return q.Label
	}
	panic(fmt.Errorf("unknown direction: %v", d))
}
func (q *QuadHash) Set(d quad.Direction, h ValueHash) {
	switch d {
	case quad.Subject:
		q.Subject = h
	case quad.Predicate:
		q.Predicate = h
	case quad.Object:
		q.Object = h
	case quad.Label:
		q.Label = h
	default:
		panic(fmt.Errorf("unknown direction: %v", d))
	}
}
<commit_msg>graph: remove deprecated Keyer type<commit_after>package graph

import (
	"encoding/hex"
	"fmt"

	"github.com/cayleygraph/quad"
)

// Ref defines an opaque "quad store reference" type. However the backend wishes
// to implement it, a Ref is merely a token to a quad or a node that the
// backing store itself understands, and the base iterators pass around.
//
// For example, in a very traditional, graphd-style graph, these are int64s
// (guids of the primitives). In a very direct sort of graph, these could be
// pointers to structs, or merely quads, or whatever works best for the
// backing store.
//
// These must be comparable, or return a comparable version on Key.
type Ref interface {
	// Key returns a dynamic type that is comparable according to the Go language specification.
	// The returned value must be unique for each receiver value.
	Key() interface{}
}

func HashOf(s quad.Value) (out ValueHash) {
	if s == nil {
		return
	}
	quad.HashTo(s, out[:])
	return
}

var _ Ref = ValueHash{}

// ValueHash is a hash of a single value.
type ValueHash [quad.HashSize]byte

func (h ValueHash) Valid() bool {
	return h != ValueHash{}
}
func (h ValueHash) Key() interface{} { return h }
func (h ValueHash) String() string {
	if !h.Valid() {
		return ""
	}
	return hex.EncodeToString(h[:])
}

// PreFetchedValue is an optional interface for graph.Ref to indicate that
// quadstore has already loaded a value into memory.
type PreFetchedValue interface {
	Ref
	NameOf() quad.Value
}

func PreFetched(v quad.Value) PreFetchedValue {
	return fetchedValue{v}
}

type fetchedValue struct {
	Val quad.Value
}

func (v fetchedValue) IsNode() bool       { return true }
func (v fetchedValue) NameOf() quad.Value { return v.Val }
func (v fetchedValue) Key() interface{}   { return v.Val }

// ToKey prepares Ref to be stored inside maps, calling Key() if necessary.
func ToKey(v Ref) interface{} {
	if v == nil {
		return nil
	}
	return v.Key()
}

var _ Ref = QuadHash{}

type QuadHash struct {
	Subject   ValueHash
	Predicate ValueHash
	Object    ValueHash
	Label     ValueHash
}

func (q QuadHash) Dirs() [4]ValueHash {
	return [4]ValueHash{
		q.Subject,
		q.Predicate,
		q.Object,
		q.Label,
	}
}
func (q QuadHash) Key() interface{} { return q }
func (q QuadHash) Get(d quad.Direction) ValueHash {
	switch d {
	case quad.Subject:
		return q.Subject
	case quad.Predicate:
		return q.Predicate
	case quad.Object:
		return q.Object
	case quad.Label:
		return q.Label
	}
	panic(fmt.Errorf("unknown direction: %v", d))
}
func (q *QuadHash) Set(d quad.Direction, h ValueHash) {
	switch d {
	case quad.Subject:
		q.Subject = h
	case quad.Predicate:
		q.Predicate = h
	case quad.Object:
		q.Object = h
	case quad.Label:
		q.Label = h
	default:
		panic(fmt.Errorf("unknown direction: %v", d))
	}
}
<|endoftext|>


<commit_before>// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff, Stani Michiels

// Load a png image and rotate it
package main

import (
	"image/color"
	"math"

	"github.com/stanim/draw2d"
	"github.com/stanim/draw2d/pdf2d"
	"github.com/stanim/gofpdf"
)

func main() {
	// Margin between the image and the frame
	const margin = 30
	// Line width od the frame
	const lineWidth = 3

	// Initialize the graphic context on an RGBA image
	dest := gofpdf.New("P", "mm", "A4", "../font")
	dest.AddPage()
	// Size of destination image
	dw, dh := dest.GetPageSize()
	gc := pdf2d.NewGraphicContext(dest)
	// Draw frame
	gc.SetFillColor(color.RGBA{0xff, 0xff, 0xff, 0xff})
	draw2d.RoundRect(gc, lineWidth, lineWidth, dw-lineWidth, dh-lineWidth, 100, 100)
	gc.SetLineWidth(lineWidth)
	gc.FillStroke()

	// load the source image
	source, err := draw2d.LoadFromPngFile("gopher.png")
	if err != nil {
		panic(err)
	}
	// Size of source image
	sw, sh := float64(source.Bounds().Dx()), float64(source.Bounds().Dy())
	// Draw image to fit in the frame
	// TODO Seems to have a transform bug here on draw image
	scale := math.Min((dw-margin*2)/sw, (dh-margin*2)/sh)
	gc.Translate(margin, margin)
	gc.Scale(scale, scale)

	gc.DrawImage(source)

	// Save to pdf
	pdf2d.SaveToPdfFile("frame-image.pdf", dest)
}
<commit_msg>moved line in frame-image<commit_after>// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff, Stani Michiels

// Load a png image and rotate it
package main

import (
	"image/color"
	"math"

	"github.com/stanim/draw2d"
	"github.com/stanim/draw2d/pdf2d"
	"github.com/stanim/gofpdf"
)

func main() {
	// Margin between the image and the frame
	const margin = 30
	// Line width od the frame
	const lineWidth = 3

	// Initialize the graphic context on an RGBA image
	dest := gofpdf.New("P", "mm", "A4", "../font")
	dest.AddPage()
	gc := pdf2d.NewGraphicContext(dest)
	// Size of destination image
	dw, dh := dest.GetPageSize()
	// Draw frame
	gc.SetFillColor(color.RGBA{0xff, 0xff, 0xff, 0xff})
	draw2d.RoundRect(gc, lineWidth, lineWidth, dw-lineWidth, dh-lineWidth, 100, 100)
	gc.SetLineWidth(lineWidth)
	gc.FillStroke()

	// load the source image
	source, err := draw2d.LoadFromPngFile("gopher.png")
	if err != nil {
		panic(err)
	}
	// Size of source image
	sw, sh := float64(source.Bounds().Dx()), float64(source.Bounds().Dy())
	// Draw image to fit in the frame
	// TODO Seems to have a transform bug here on draw image
	scale := math.Min((dw-margin*2)/sw, (dh-margin*2)/sh)
	gc.Translate(margin, margin)
	gc.Scale(scale, scale)

	gc.DrawImage(source)

	// Save to pdf
	pdf2d.SaveToPdfFile("frame-image.pdf", dest)
}
<|endoftext|>


<commit_before>package storage

import (
	"github.com/ActiveState/log"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

type Tracker interface {
	Update(instKey string, childKey string, childVal int64)
	LoadTailers()
	Remove(key string)
	RegisterInstance(instKey string)
	InitializeChildNode(instKey string, childkey string, offSet int64)
	Submit()
	StartSubmissionTimer(retentionPeriod time.Duration)
	IsInstanceRegistered(instKey string) bool
	IsChildNodeInitialized(instKey string, childkey string) bool
	GetFileCachedOffset(instkey string, fname string) int64
}

type TailNode map[string]int64

type Tailer struct {
	Instances map[string]TailNode
}

type tracker struct {
	storage       Storage
	Cached        *Tailer // do not expose this, it should ONLY be updated via Tracker methods
	mux           *sync.Mutex
	timerStopChan chan struct{} // used to send quit signal to timer
}

var (
	MinIOTicker = 5 * time.Second
)

func NewTracker(s Storage) Tracker {
	return &tracker{
		storage: s,
		mux:     &sync.Mutex{},
		Cached: &Tailer{
			Instances: make(map[string]TailNode),
		},
		timerStopChan: make(chan struct{}),
	}
}

func (t *tracker) StartSubmissionTimer(retentionPeriod time.Duration) {
	if retentionPeriod.Seconds() <= MinIOTicker.Seconds() {
		seconds := retentionPeriod / (1000 * time.Millisecond)
		log.Warnf("IMPORTANT: Setting retention period to %ds will increase your IO Rate", seconds)

	}
	ticker := time.NewTicker(retentionPeriod)
	go func() {
		for {
			select {
			case <-ticker.C:
				t.Submit()
			case <-t.timerStopChan:
				ticker.Stop()
				return
			}

		}
	}()
}

func (t *tracker) RegisterInstance(instKey string) {
	t.mux.Lock()
	if _, instance_exist := t.Cached.Instances[instKey]; !instance_exist {
		t.Cached.Instances[instKey] = TailNode{}
		log.Info("Current status : ", t.Cached.Instances)
	}
	t.mux.Unlock()
}

// this is mainly used for testing since we are not exposing Cached via interface
func (t *tracker) IsInstanceRegistered(instKey string) bool {
	var exist bool
	t.mux.Lock()
	if _, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		exist = instance_exist
	}
	t.mux.Unlock()
	return exist
}

func (t *tracker) IsChildNodeInitialized(instKey string, childkey string) bool {
	var exist bool
	t.mux.Lock()
	if tailNode, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		if _, childNode_exist := tailNode[childkey]; childNode_exist {
			exist = childNode_exist
		}
	}
	t.mux.Unlock()
	return exist
}

func (t *tracker) InitializeChildNode(instKey string, childkey string, offSet int64) {
	t.mux.Lock()
	if tailNode, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		if _, childNode_exist := tailNode[childkey]; !childNode_exist {
			tailNode[childkey] = offSet
			t.Cached.Instances[instKey] = tailNode
			log.Info("Current status : ", t.Cached.Instances)
		}
	}
	t.mux.Unlock()
	runtime.Gosched()
}

func (t *tracker) GetFileCachedOffset(instkey string, fname string) int64 {
	var offset int64
	t.mux.Lock()
	if tailNode, instance_exist := t.Cached.Instances[instkey]; instance_exist {
		offset = tailNode[fname]
	}
	t.mux.Unlock()
	runtime.Gosched()
	return offset
}

func (t *tracker) Update(instKey string, childKey string, childVal int64) {
	var offset int64 = 0
	if tailNode, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		if _, childNode_exist := tailNode[childKey]; childNode_exist {
			atomic.StoreInt64(&offset, childVal)
			tailNode[childKey] = offset
		}
	}
}

func (t *tracker) Remove(key string) {
	log.Info("Removing the following key %s from cached instances", key)
	t.mux.Lock()
	delete(t.Cached.Instances, key)
	t.mux.Unlock()
	t.Submit()
}

func (t *tracker) LoadTailers() {
	t.mux.Lock()
	t.storage.Load(&t.Cached)
	log.Info("Loaded the following tailers from previous session:", t.Cached.Instances)
	t.mux.Unlock()
}

func (t *tracker) Submit() {
	t.mux.Lock()
	log.Info("Storing the offset in the following instances:", t.Cached.Instances)
	t.storage.Write(t.Cached)
	t.mux.Unlock()
}
<commit_msg>300409-updated the tailnode offset to an struct boxedInt64 so we can refrence the address during atomic store<commit_after>package storage

import (
	"github.com/ActiveState/log"
	"runtime"
	"sync"
	"sync/atomic"
	"time"
)

type Tracker interface {
	Update(instKey string, childKey string, childVal int64)
	LoadTailers()
	Remove(key string)
	RegisterInstance(instKey string)
	InitializeChildNode(instKey string, childkey string, offSet int64)
	Submit()
	StartSubmissionTimer(retentionPeriod time.Duration)
	IsInstanceRegistered(instKey string) bool
	IsChildNodeInitialized(instKey string, childkey string) bool
	GetFileCachedOffset(instkey string, fname string) int64
}

type boxedInt64 struct { v int64}

type TailNode map[string]*boxedInt64

type Tailer struct {
	Instances map[string]TailNode
}

type tracker struct {
	storage       Storage
	Cached        *Tailer // do not expose this, it should ONLY be updated via Tracker methods
	mux           *sync.Mutex
	timerStopChan chan struct{} // used to send quit signal to timer
}

var (
	MinIOTicker = 5 * time.Second
)

func NewTracker(s Storage) Tracker {
	return &tracker{
		storage: s,
		mux:     &sync.Mutex{},
		Cached: &Tailer{
			Instances: make(map[string]TailNode),
		},
		timerStopChan: make(chan struct{}),
	}
}

func (t *tracker) StartSubmissionTimer(retentionPeriod time.Duration) {
	if retentionPeriod.Seconds() <= MinIOTicker.Seconds() {
		seconds := retentionPeriod / (1000 * time.Millisecond)
		log.Warnf("IMPORTANT: Setting retention period to %ds will increase your IO Rate", seconds)

	}
	ticker := time.NewTicker(retentionPeriod)
	go func() {
		for {
			select {
			case <-ticker.C:
				t.Submit()
			case <-t.timerStopChan:
				ticker.Stop()
				return
			}

		}
	}()
}

func (t *tracker) RegisterInstance(instKey string) {
	t.mux.Lock()
	if _, instance_exist := t.Cached.Instances[instKey]; !instance_exist {
		t.Cached.Instances[instKey] = TailNode{}
		log.Info("Current status : ", t.Cached.Instances)
	}
	t.mux.Unlock()
}

// this is mainly used for testing since we are not exposing Cached via interface
func (t *tracker) IsInstanceRegistered(instKey string) bool {
	var exist bool
	t.mux.Lock()
	if _, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		exist = instance_exist
	}
	t.mux.Unlock()
	return exist
}

func (t *tracker) IsChildNodeInitialized(instKey string, childkey string) bool {
	var exist bool
	t.mux.Lock()
	if tailNode, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		if _, childNode_exist := tailNode[childkey]; childNode_exist {
			exist = childNode_exist
		}
	}
	t.mux.Unlock()
	return exist
}

func (t *tracker) InitializeChildNode(instKey string, childkey string, offSet int64) {
	t.mux.Lock()
	if tailNode, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		if _, childNode_exist := tailNode[childkey]; !childNode_exist {
			tailNode[childkey] = &boxedInt64{v: offSet}
			t.Cached.Instances[instKey] = tailNode
			log.Info("Current status : ", t.Cached.Instances)
		}
	}
	t.mux.Unlock()
	runtime.Gosched()
}

func (t *tracker) GetFileCachedOffset(instkey string, fname string) int64 {
	var offset int64
	t.mux.Lock()
	if tailNode, instance_exist := t.Cached.Instances[instkey]; instance_exist {
		offset = tailNode[fname].v
	}
	t.mux.Unlock()
	runtime.Gosched()
	return offset
}

func (t *tracker) Update(instKey string, childKey string, childVal int64) {
	if tailNode, instance_exist := t.Cached.Instances[instKey]; instance_exist {
		if _, childNode_exist := tailNode[childKey]; childNode_exist {
			atomic.StoreInt64(&tailNode[childKey].v, childVal)
		}
	}
}

func (t *tracker) Remove(key string) {
	log.Info("Removing the following key %s from cached instances", key)
	t.mux.Lock()
	delete(t.Cached.Instances, key)
	t.mux.Unlock()
	t.Submit()
}

func (t *tracker) LoadTailers() {
	t.mux.Lock()
	t.storage.Load(&t.Cached)
	log.Info("Loaded the following tailers from previous session:", t.Cached.Instances)
	t.mux.Unlock()
}

func (t *tracker) Submit() {
	t.mux.Lock()
	log.Info("Storing the offset in the following instances:", t.Cached.Instances)
	t.storage.Write(t.Cached)
	t.mux.Unlock()
}
<|endoftext|>