MyMarshaler fully impelmented

This commit is contained in:
Radu Ioan Fericean
2012-07-04 19:16:08 +03:00
parent 7dbf7f90b1
commit bb2916c42c
10 changed files with 312 additions and 34 deletions

View File

@@ -204,9 +204,9 @@ Serializes the action timing for the storage. Used for key-value storages.
func (at *ActionTiming) store() (result string) {
result += at.Tag + "|"
for _, ubi := range at.UserBalanceIds {
result += ubi + ";"
result += ubi + ","
}
result = strings.TrimRight(result, ";") + "|"
result = strings.TrimRight(result, ",") + "|"
result += at.Timing.store() + "|"
result += strconv.FormatFloat(at.Weight, 'f', -1, 64) + "|"
result += at.ActionsId
@@ -219,9 +219,10 @@ De-serializes the action timing for the storage. Used for key-value storages.
func (at *ActionTiming) restore(input string) {
elements := strings.Split(input, "|")
at.Tag = elements[0]
for _, ubi := range strings.Split(elements[1], ";") {
for _, ubi := range strings.Split(elements[1], ",") {
at.UserBalanceIds = append(at.UserBalanceIds, ubi)
}
at.Timing = &Interval{}
at.Timing.restore(elements[2])
at.Weight, _ = strconv.ParseFloat(elements[3], 64)

View File

@@ -21,6 +21,8 @@ package timespans
import (
"log"
"sort"
"strconv"
"strings"
)
type ActionTrigger struct {
@@ -73,3 +75,30 @@ func (atpl ActionTriggerPriotityList) Less(i, j int) bool {
func (atpl ActionTriggerPriotityList) Sort() {
sort.Sort(atpl)
}
/*
Serializes the action trigger for the storage. Used for key-value storages.
*/
func (at *ActionTrigger) store() (result string) {
result += at.BalanceId + ";"
result += at.DestinationId + ";"
result += at.ActionsId + ";"
result += strconv.FormatFloat(at.ThresholdValue, 'f', -1, 64) + ";"
result += strconv.FormatFloat(at.Weight, 'f', -1, 64)
return
}
/*
De-serializes the action timing for the storage. Used for key-value storages.
*/
func (at *ActionTrigger) restore(input string) {
elements := strings.Split(input, ";")
if len(elements) != 5 {
return
}
at.BalanceId = elements[0]
at.DestinationId = elements[1]
at.ActionsId = elements[2]
at.ThresholdValue, _ = strconv.ParseFloat(elements[3], 64)
at.Weight, _ = strconv.ParseFloat(elements[4], 64)
}

View File

@@ -44,7 +44,7 @@ func TestActionTimingStoreRestore(t *testing.T) {
ActionsId: "Commando",
}
r := at.store()
if string(r) != "test|one;two;three|1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1|10|Commando" {
if string(r) != "test|one,two,three|1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;1|10|Commando" {
t.Errorf("Error serializing action timing: %v", string(r))
}
o := &ActionTiming{}
@@ -54,6 +54,25 @@ func TestActionTimingStoreRestore(t *testing.T) {
}
}
func TestActionTriggerStoreRestore(t *testing.T) {
at := &ActionTrigger{
BalanceId: CREDIT,
ThresholdValue: 100.0,
DestinationId: "NAT",
Weight: 10.0,
ActionsId: "Commando",
}
r := at.store()
if string(r) != "MONETARY;NAT;Commando;100;10" {
t.Errorf("Error serializing action trigger: %v", string(r))
}
o := &ActionTrigger{}
o.restore(r)
if !reflect.DeepEqual(o, at) {
t.Errorf("Expected %v was %v", at, o)
}
}
func TestActionTimingNothing(t *testing.T) {
at := &ActionTiming{}
st := at.GetNextStartTime()

View File

@@ -69,6 +69,7 @@ func (ap *ActivationPeriod) store() (result string) {
for _, i := range ap.Intervals {
result += i.store() + "|"
}
result = strings.TrimRight(result, "|")
return
}

View File

@@ -42,7 +42,7 @@ func TestApStoreRestore(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result := ap.store()
expected := "1328106601000000000|2;1;3,4;14:30:00;15:00:00;0;0;0;0|"
expected := "1328106601000000000|2;1;3,4;14:30:00;15:00:00;0;0;0;0"
if result != expected {
t.Errorf("Expected %q was %q", expected, result)
}
@@ -166,14 +166,7 @@ func TestApAddIntervalIfNotPresent(t *testing.T) {
/**************************** Benchmarks *************************************/
func BenchmarkActivationPeriodRestore(b *testing.B) {
ap := ActivationPeriod{}
for i := 0; i < b.N; i++ {
json.Unmarshal([]byte("1328106601;2|1|3,4|14:30:00|15:00:00|0|0|0|0;"), &ap)
}
}
func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
func BenchmarkActivationPeriodStoreRestoreJson(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
@@ -191,3 +184,29 @@ func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
json.Unmarshal(result, &ap1)
}
}
func BenchmarkActivationPeriodRestore(b *testing.B) {
ap := ActivationPeriod{}
for i := 0; i < b.N; i++ {
ap.restore("1328106601000000000|2;1;3,4;14:30:00;15:00:00;0;0;0;0")
}
}
func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
b.StopTimer()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Months: []time.Month{time.February},
MonthDays: []int{1},
WeekDays: []time.Weekday{time.Wednesday, time.Thursday},
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap1 := ActivationPeriod{}
b.StartTimer()
for i := 0; i < b.N; i++ {
result := ap.store()
ap1.restore(result)
}
}

View File

@@ -23,6 +23,7 @@ import (
"encoding/gob"
"encoding/json"
"errors"
"log"
"strings"
)
@@ -194,6 +195,7 @@ func (mm *MyMarshaler) Unmarshal(data []byte, v interface{}) (err error) {
return nil
}
log.Print("Using default gob marshalling!")
mm.buf.Reset()
mm.buf.Write(data)
return gob.NewDecoder(&mm.buf).Decode(v)

View File

@@ -0,0 +1,82 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012 Radu Ioan Fericean
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package timespans
import (
"strconv"
"strings"
)
// Amount of a trafic of a certain type
type UnitsCounter struct {
Direction string
BalanceId string
Units float64
Weight float64
MinuteBuckets []*MinuteBucket
}
// Structure to store actions according to weight
type countersorter []*UnitsCounter
func (s countersorter) Len() int {
return len(s)
}
func (s countersorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s countersorter) Less(i, j int) bool {
return s[i].Weight < s[j].Weight
}
/*
Serializes the unit counter for the storage. Used for key-value storages.
*/
func (uc *UnitsCounter) store() (result string) {
result += uc.Direction + "/"
result += uc.BalanceId + "/"
result += strconv.FormatFloat(uc.Units, 'f', -1, 64) + "/"
result += strconv.FormatFloat(uc.Weight, 'f', -1, 64) + "/"
for _, mb := range uc.MinuteBuckets {
result += mb.store() + ","
}
result = strings.TrimRight(result, ",")
return
}
/*
De-serializes the unit counter for the storage. Used for key-value storages.
*/
func (uc *UnitsCounter) restore(input string) {
elements := strings.Split(input, "/")
if len(elements) != 5 {
return
}
uc.Direction = elements[0]
uc.BalanceId = elements[1]
uc.Units, _ = strconv.ParseFloat(elements[2], 64)
uc.Weight, _ = strconv.ParseFloat(elements[3], 64)
for _, mbs := range strings.Split(elements[4], ",") {
mb := &MinuteBucket{}
mb.restore(mbs)
uc.MinuteBuckets = append(uc.MinuteBuckets, mb)
}
}

View File

@@ -0,0 +1,43 @@
/*
Rating system designed to be used in VoIP Carriers World
Copyright (C) 2012 Radu Ioan Fericean
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package timespans
import (
"reflect"
"testing"
)
func TestUnitsCounterStoreRestore(t *testing.T) {
uc := &UnitsCounter{
Direction: OUTBOUND,
BalanceId: SMS,
Units: 100,
Weight: 10,
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}},
}
r := uc.store()
if string(r) != "OUT/SMS/100/10/0;20;1;0;NAT,0;10;10;0;RET" {
t.Errorf("Error serializing units counter: %v", string(r))
}
o := &UnitsCounter{}
o.restore(r)
if !reflect.DeepEqual(o, uc) {
t.Errorf("Expected %v was %v", uc, o)
}
}

View File

@@ -19,7 +19,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package timespans
import (
// "log"
"strconv"
"strings"
"sync"
)
@@ -291,26 +292,72 @@ Resets the user balance items to their tariff plan values.
return
}
*/
// Amount of a trafic of a certain type
type UnitsCounter struct {
Direction string
BalanceId string
Units float64
Weight float64
MinuteBuckets []*MinuteBucket
/*
Serializes the user balance for the storage. Used for key-value storages.
*/
func (ub *UserBalance) store() (result string) {
result += ub.Id + "|"
result += ub.Type + "|"
for k, v := range ub.BalanceMap {
result += k + ":" + strconv.FormatFloat(v, 'f', -1, 64) + "#"
}
result = strings.TrimRight(result, "#") + "|"
for _, mb := range ub.MinuteBuckets {
result += mb.store() + "#"
}
result = strings.TrimRight(result, "#") + "|"
for _, uc := range ub.UnitCounters {
result += uc.store() + "#"
}
result = strings.TrimRight(result, "#") + "|"
for _, at := range ub.ActionTriggers {
result += at.store() + "#"
}
result = strings.TrimRight(result, "#")
return
}
// Structure to store actions according to weight
type countersorter []*UnitsCounter
func (s countersorter) Len() int {
return len(s)
}
func (s countersorter) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s countersorter) Less(i, j int) bool {
return s[i].Weight < s[j].Weight
/*
De-serializes the user balance for the storage. Used for key-value storages.
*/
func (ub *UserBalance) restore(input string) {
elements := strings.Split(input, "|")
ub.Id = elements[0]
ub.Type = elements[1]
if ub.BalanceMap == nil {
ub.BalanceMap = make(map[string]float64, 0)
}
for _, maps := range strings.Split(elements[2], "#") {
kv := strings.Split(maps, ":")
if len(kv) != 2 {
continue
}
value, _ := strconv.ParseFloat(kv[1], 64)
ub.BalanceMap[kv[0]] = value
}
for _, mbs := range strings.Split(elements[3], "#") {
if mbs == "" {
continue
}
mb := &MinuteBucket{}
mb.restore(mbs)
ub.MinuteBuckets = append(ub.MinuteBuckets, mb)
}
for _, ucs := range strings.Split(elements[4], "#") {
if ucs == "" {
continue
}
uc := &UnitsCounter{}
uc.restore(ucs)
ub.UnitCounters = append(ub.UnitCounters, uc)
}
for _, ats := range strings.Split(elements[5], "#") {
if ats == "" {
continue
}
at := &ActionTrigger{}
at.restore(ats)
ub.ActionTriggers = append(ub.ActionTriggers, at)
}
}

View File

@@ -35,6 +35,41 @@ func init() {
}
func TestUserBalanceStoreRestore(t *testing.T) {
uc := &UnitsCounter{
Direction: OUTBOUND,
BalanceId: SMS,
Units: 100,
Weight: 10,
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}},
}
at := &ActionTrigger{
BalanceId: CREDIT,
ThresholdValue: 100.0,
DestinationId: "NAT",
Weight: 10.0,
ActionsId: "Commando",
}
ub := &UserBalance{
Id: "rif",
Type: UB_TYPE_POSTPAID,
BalanceMap: map[string]float64{SMS: 14, TRAFFIC: 1024},
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, Percent: 0, DestinationId: "RET"}},
UnitCounters: []*UnitsCounter{uc, uc},
ActionTriggers: ActionTriggerPriotityList{at, at, at},
}
r := ub.store()
if string(r) != "rif|postpaid|SMS:14#INTERNET:1024|0;20;1;0;NAT#0;10;10;0;RET|OUT/SMS/100/10/0;20;1;0;NAT,0;10;10;0;RET#OUT/SMS/100/10/0;20;1;0;NAT,0;10;10;0;RET|MONETARY;NAT;Commando;100;10#MONETARY;NAT;Commando;100;10#MONETARY;NAT;Commando;100;10" &&
string(r) != "rif|postpaid|INTERNET:1024#SMS:14|0;20;1;0;NAT#0;10;10;0;RET|OUT/SMS/100/10/0;20;1;0;NAT,0;10;10;0;RET#OUT/SMS/100/10/0;20;1;0;NAT,0;10;10;0;RET|MONETARY;NAT;Commando;100;10#MONETARY;NAT;Commando;100;10#MONETARY;NAT;Commando;100;10" {
t.Errorf("Error serializing action timing: %v", string(r))
}
o := &UserBalance{}
o.restore(r)
if !reflect.DeepEqual(o, ub) {
t.Errorf("Expected %v was %v", ub, o)
}
}
func TestUserBalanceStorageStoreRestore(t *testing.T) {
b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"}
b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"}
rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]float64{CREDIT: 21}}