revrted to my own serialization/deserialization

This commit is contained in:
Radu Ioan Fericean
2012-03-02 18:13:45 +02:00
parent 6b5d0fa5dd
commit 4e8cc3e5b8
10 changed files with 331 additions and 174 deletions

View File

@@ -20,6 +20,8 @@ package timespans
import (
"time"
//"log"
"strconv"
"strings"
)
/*
@@ -40,3 +42,57 @@ func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
}
}
/*
Serializes the activation periods for the storage. Used for key-value storages.
*/
func (ap *ActivationPeriod) store() (result string) {
result += strconv.FormatInt(ap.ActivationTime.UnixNano(), 10) + ";"
for _, i := range ap.Intervals {
var is string
is = strconv.Itoa(int(i.Month)) + "|"
is += strconv.Itoa(i.MonthDay) + "|"
for _, wd := range i.WeekDays {
is += strconv.Itoa(int(wd)) + ","
}
is = strings.TrimRight(is, ",") + "|"
is += i.StartTime + "|"
is += i.EndTime + "|"
is += strconv.FormatFloat(i.Ponder, 'f', -1, 64) + "|"
is += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + "|"
is += strconv.FormatFloat(i.Price, 'f', -1, 64) + "|"
is += strconv.FormatFloat(i.BillingUnit, 'f', -1, 64)
result += is + ";"
}
return
}
/*
De-serializes the activation periods for the storage. Used for key-value storages.
*/
func (ap *ActivationPeriod) restore(input string) {
elements := strings.Split(input, ";")
unixNano, _ := strconv.ParseInt(elements[0], 10, 64)
ap.ActivationTime = time.Unix(0, unixNano).In(time.UTC)
ap.Intervals = make([]*Interval, 0)
for _, is := range elements[1 : len(elements)-1] {
i := &Interval{}
ise := strings.Split(is, "|")
month, _ := strconv.Atoi(ise[0])
i.Month = time.Month(month)
i.MonthDay, _ = strconv.Atoi(ise[1])
for _, d := range strings.Split(ise[2], ",") {
if d != "" {
wd, _ := strconv.Atoi(d)
i.WeekDays = append(i.WeekDays, time.Weekday(wd))
}
}
i.StartTime = ise[3]
i.EndTime = ise[4]
i.Ponder, _ = strconv.ParseFloat(ise[5], 64)
i.ConnectFee, _ = strconv.ParseFloat(ise[6], 64)
i.Price, _ = strconv.ParseFloat(ise[7], 64)
i.BillingUnit, _ = strconv.ParseFloat(ise[8], 64)
ap.Intervals = append(ap.Intervals, i)
}
}

View File

@@ -47,8 +47,6 @@ func TestApRestoreRedis(t *testing.T) {
}
func TestApStoreRestore(t *testing.T) {
getter, _ := NewKyotoStorage("test.kch")
defer getter.Close()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Month: time.February,
MonthDay: 1,
@@ -57,22 +55,29 @@ func TestApStoreRestore(t *testing.T) {
EndTime: "15:00:00"}
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
getter.SetActivationPeriods("storerestore", []*ActivationPeriod{ap})
aps, err := getter.GetActivationPeriods("storerestore")
if err != nil || len(aps) != 1 || !reflect.DeepEqual(ap, aps[0]) {
t.Log(aps)
t.Errorf("Expected %v was %v ", ap, aps)
result := ap.store()
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)
}
ap1 := ActivationPeriod{}
ap1.restore(result)
if reflect.DeepEqual(ap, ap1) {
t.Errorf("Expected %v was %v", ap, ap1)
}
}
/**************************** Benchmarks *************************************/
func BenchmarkActivationPeriodRestore(b *testing.B) {
ap := ActivationPeriod{}
for i := 0; i < b.N; i++ {
ap.restore("1328106601;2|1|3,4|14:30:00|15:00:00|0|0|0|0;")
}
}
func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
b.StopTimer()
getter, _ := NewKyotoStorage("test.kch")
defer getter.Close()
d := time.Date(2012, time.February, 1, 14, 30, 1, 0, time.UTC)
i := &Interval{Month: time.February,
MonthDay: 1,
@@ -82,9 +87,10 @@ func BenchmarkActivationPeriodStoreRestore(b *testing.B) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
ap1 := ActivationPeriod{}
b.StartTimer()
for i := 0; i < b.N; i++ {
getter.SetActivationPeriods("storerestore", []*ActivationPeriod{ap})
getter.GetActivationPeriods("storerestore")
result := ap.store()
ap1.restore(result)
}
}

View File

@@ -221,7 +221,7 @@ func TestMaxSessionTimeNoUserBudget(t *testing.T) {
defer getter.Close()
cd := &CallDescriptor{CstmId: "vdf", Subject: "rif", DestinationPrefix: "0723", storageGetter: getter, Amount: 1000}
result, err := cd.GetMaxSessionTime()
if result != 1000 || err == nil {
if result != 1000 || err != nil {
t.Errorf("Expected %v was %v", 1000, result)
}
}

View File

@@ -17,6 +17,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package timespans
import (
"strings"
)
/*
Structure that gathers multiple destination prefixes under a common id.
*/
@@ -25,6 +29,21 @@ type Destination struct {
Prefixes []string
}
/*
Serializes the destination for the storage. Used for key-value storages.
*/
func (d *Destination) store() (result string) {
for _, p := range d.Prefixes {
result += p + ","
}
result = strings.TrimRight(result, ",")
return
}
func (d *Destination) restore(input string) {
d.Prefixes = strings.Split(input, ",")
}
/*
De-serializes the destination for the storage. Used for key-value storages.
*/

View File

@@ -15,123 +15,83 @@ 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 (
"bitbucket.org/ww/cabinet"
"bytes"
"encoding/json"
"github.com/fsouza/gokabinet/kc"
//"log"
"sync"
"strings"
)
type KyotoStorage struct {
//db *kc.DB
db *cabinet.KCDB
buf bytes.Buffer
dec *json.Decoder
enc *json.Encoder
mux sync.Mutex // we need norma lock because we reset the buf variable
db *kc.DB
}
func NewKyotoStorage(filaName string) (*KyotoStorage, error) {
ndb := cabinet.New()
err := ndb.Open(filaName, cabinet.KCOWRITER|cabinet.KCOCREATE)
ks := &KyotoStorage{db: ndb}
ks.dec = json.NewDecoder(&ks.buf)
ks.enc = json.NewEncoder(&ks.buf)
return ks, err
ndb, err := kc.Open(filaName, kc.WRITE)
return &KyotoStorage{db: ndb}, err
}
func (ks *KyotoStorage) Close() {
ks.db.Close()
}
func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
ks.mux.Lock()
defer ks.mux.Unlock()
func (ks *KyotoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
values, err := ks.db.Get(key)
ks.buf.Reset()
ks.enc.Encode(aps)
return ks.db.Set([]byte(key), ks.buf.Bytes())
if err == nil {
for _, ap_string := range strings.Split(values, "\n") {
if len(ap_string) > 0 {
ap := &ActivationPeriod{}
ap.restore(ap_string)
aps = append(aps, ap)
}
}
}
return aps, err
}
func (ks *KyotoStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
ks.mux.Lock()
defer ks.mux.Unlock()
func (ks *KyotoStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
result := ""
for _, ap := range aps {
result += ap.store() + "\n"
}
return ks.db.Set(key, result)
}
values, err := ks.db.Get([]byte(key))
if err == nil {
ks.buf.Reset()
ks.buf.Write(values)
err = ks.dec.Decode(&aps)
func (ks *KyotoStorage) GetDestination(key string) (dest *Destination, err error) {
if values, err := ks.db.Get(key); err == nil {
dest = &Destination{Id: key}
dest.restore(values)
}
return
}
func (ks *KyotoStorage) SetDestination(dest *Destination) error {
ks.mux.Lock()
defer ks.mux.Unlock()
ks.buf.Reset()
ks.enc.Encode(dest)
return ks.db.Set([]byte(dest.Id), ks.buf.Bytes())
return ks.db.Set(dest.Id, dest.store())
}
func (ks *KyotoStorage) GetDestination(key string) (dest *Destination, err error) {
ks.mux.Lock()
defer ks.mux.Unlock()
values, err := ks.db.Get([]byte(key))
if err == nil {
ks.buf.Reset()
ks.buf.Write(values)
err = ks.dec.Decode(&dest)
func (ks *KyotoStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
if values, err := ks.db.Get(key); err == nil {
tp = &TariffPlan{Id: key}
tp.restore(values)
}
return
}
func (ks *KyotoStorage) SetTariffPlan(tp *TariffPlan) error {
ks.mux.Lock()
defer ks.mux.Unlock()
ks.buf.Reset()
ks.enc.Encode(tp)
return ks.db.Set([]byte(tp.Id), ks.buf.Bytes())
return ks.db.Set(tp.Id, tp.store())
}
func (ks *KyotoStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
ks.mux.Lock()
defer ks.mux.Unlock()
values, err := ks.db.Get([]byte(key))
if err == nil {
ks.buf.Reset()
ks.buf.Write(values)
err = ks.dec.Decode(&tp)
func (ks *KyotoStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
if values, err := ks.db.Get(key); err == nil {
ub = &UserBudget{Id: key}
ub.restore(values)
}
return
}
func (ks *KyotoStorage) SetUserBudget(ub *UserBudget) error {
ks.mux.Lock()
defer ks.mux.Unlock()
ks.buf.Reset()
ks.enc.Encode(ub)
return ks.db.Set([]byte(ub.Id), ks.buf.Bytes())
}
func (ks *KyotoStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
ks.mux.Lock()
defer ks.mux.Unlock()
values, err := ks.db.Get([]byte(key))
if err == nil {
ks.buf.Reset()
ks.buf.Write(values)
ks.dec.Decode(&ub)
}
return
return ks.db.Set(ub.Id, ub.store())
}

View File

@@ -20,6 +20,8 @@ package timespans
import (
// "log"
"math"
"strconv"
"strings"
)
type MinuteBucket struct {
@@ -31,6 +33,28 @@ type MinuteBucket struct {
precision int
}
/*
Serializes the minute bucket for the storage. Used for key-value storages.
*/
func (mb *MinuteBucket) store() (result string) {
result += strconv.Itoa(int(mb.Seconds)) + "|"
result += strconv.Itoa(int(mb.Priority)) + "|"
result += strconv.FormatFloat(mb.Price, 'f', -1, 64) + "|"
result += mb.DestinationId
return
}
/*
De-serializes the minute bucket for the storage. Used for key-value storages.
*/
func (mb *MinuteBucket) restore(input string) {
elements := strings.Split(input, "|")
mb.Seconds, _ = strconv.ParseFloat(elements[0], 64)
mb.Priority, _ = strconv.Atoi(elements[1])
mb.Price, _ = strconv.ParseFloat(elements[2], 64)
mb.DestinationId = elements[3]
}
/*
Returns the destination loading it from the storage if necessary.
*/

View File

@@ -18,126 +18,86 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package timespans
import (
"bytes"
"encoding/json"
"github.com/simonz05/godis"
// "log"
"sync"
"strings"
)
type RedisStorage struct {
dbNb int
db *godis.Client
buf bytes.Buffer
dec *json.Decoder
enc *json.Encoder
mux sync.Mutex
}
func NewRedisStorage(address string, db int) (*RedisStorage, error) {
ndb := godis.New(address, db, "")
rs := &RedisStorage{db: ndb, dbNb: db}
rs.dec = json.NewDecoder(&rs.buf)
rs.enc = json.NewEncoder(&rs.buf)
return rs, nil
return &RedisStorage{db: ndb, dbNb: db}, nil
}
func (rs *RedisStorage) Close() {
rs.db.Quit()
}
func (rs *RedisStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
//.db.Select(rs.dbNb)
rs.mux.Lock()
defer rs.mux.Unlock()
rs.buf.Reset()
rs.enc.Encode(aps)
return rs.db.Set(key, rs.buf.Bytes())
}
func (rs *RedisStorage) GetActivationPeriods(key string) (aps []*ActivationPeriod, err error) {
//rs.db.Select(rs.dbNb)
rs.mux.Lock()
defer rs.mux.Unlock()
elem, err := rs.db.Get(key)
values := elem.String()
if err == nil {
rs.buf.Reset()
rs.buf.Write(elem.Bytes())
err = rs.dec.Decode(&aps)
for _, ap_string := range strings.Split(values, "\n") {
if len(ap_string) > 0 {
ap := &ActivationPeriod{}
ap.restore(ap_string)
aps = append(aps, ap)
}
}
}
return
return aps, err
}
func (rs *RedisStorage) SetDestination(dest *Destination) error {
//rs.db.Select(rs.dbNb + 1)
rs.mux.Lock()
defer rs.mux.Unlock()
rs.buf.Reset()
rs.enc.Encode(dest)
return rs.db.Set(dest.Id, rs.buf.Bytes())
func (rs *RedisStorage) SetActivationPeriods(key string, aps []*ActivationPeriod) error {
//.db.Select(rs.dbNb)
result := ""
for _, ap := range aps {
result += ap.store() + "\n"
}
return rs.db.Set(key, result)
}
func (rs *RedisStorage) GetDestination(key string) (dest *Destination, err error) {
//rs.db.Select(rs.dbNb + 1)
rs.mux.Lock()
defer rs.mux.Unlock()
if values, err := rs.db.Get(key); err == nil {
dest = &Destination{Id: key}
dest.restore(values.String())
}
return
}
func (rs *RedisStorage) SetDestination(dest *Destination) error {
//rs.db.Select(rs.dbNb + 1)
return rs.db.Set(dest.Id, dest.store())
}
elem, err := rs.db.Get(key)
if err == nil {
rs.buf.Reset()
rs.buf.Write(elem.Bytes())
err = rs.dec.Decode(&dest)
func (rs *RedisStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
//rs.db.Select(rs.dbNb + 2)
if values, err := rs.db.Get(key); err == nil {
tp = &TariffPlan{Id: key}
tp.restore(values.String())
}
return
}
func (rs *RedisStorage) SetTariffPlan(tp *TariffPlan) error {
//rs.db.Select(rs.dbNb + 2)
rs.mux.Lock()
defer rs.mux.Unlock()
rs.buf.Reset()
rs.enc.Encode(tp)
return rs.db.Set(tp.Id, rs.buf.Bytes())
return rs.db.Set(tp.Id, tp.store())
}
func (rs *RedisStorage) GetTariffPlan(key string) (tp *TariffPlan, err error) {
//rs.db.Select(rs.dbNb + 2)
rs.mux.Lock()
defer rs.mux.Unlock()
elem, err := rs.db.Get(key)
if err == nil {
rs.buf.Reset()
rs.buf.Write(elem.Bytes())
err = rs.dec.Decode(&tp)
func (rs *RedisStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
//rs.db.Select(rs.dbNb + 3)
if values, err := rs.db.Get(key); err == nil {
ub = &UserBudget{Id: key}
ub.restore(values.String())
}
return
}
func (rs *RedisStorage) SetUserBudget(ub *UserBudget) error {
//rs.db.Select(rs.dbNb + 3)
rs.mux.Lock()
defer rs.mux.Unlock()
rs.buf.Reset()
rs.enc.Encode(ub)
return rs.db.Set(ub.Id, rs.buf.Bytes())
}
func (rs *RedisStorage) GetUserBudget(key string) (ub *UserBudget, err error) {
//rs.db.Select(rs.dbNb + 3)
rs.mux.Lock()
defer rs.mux.Unlock()
elem, err := rs.db.Get(key)
if err == nil {
rs.buf.Reset()
rs.buf.Write(elem.Bytes())
err = rs.dec.Decode(&ub)
}
return
return rs.db.Set(ub.Id, ub.store())
}

View File

@@ -19,6 +19,8 @@ package timespans
import (
// "log"
"strings"
"strconv"
)
/*
@@ -35,6 +37,62 @@ type TariffPlan struct {
VolumeDiscountThresholds []*VolumeDiscount
}
/*
Serializes the tariff plan for the storage. Used for key-value storages.
*/
func (tp *TariffPlan) store() (result string) {
result += strconv.FormatFloat(tp.SmsCredit, 'f', -1, 64) + ";"
result += strconv.FormatFloat(tp.Traffic, 'f', -1, 64) + ";"
result += strconv.FormatFloat(tp.ReceivedCallSecondsLimit, 'f', -1, 64) + ";"
if tp.RecivedCallBonus == nil {
tp.RecivedCallBonus = &RecivedCallBonus{}
}
result += tp.RecivedCallBonus.store() + ";"
for i, mb := range tp.MinuteBuckets {
if i > 0 {
result += ","
}
result += mb.store()
}
if tp.VolumeDiscountThresholds != nil {
result += ";"
}
for i, vd := range tp.VolumeDiscountThresholds {
if i > 0 {
result += ","
}
result += strconv.FormatFloat(vd.Volume, 'f', -1, 64) + "|" + strconv.FormatFloat(vd.Discount, 'f', -1, 64)
}
result = strings.TrimRight(result, ";")
return
}
/*
De-serializes the tariff plan for the storage. Used for key-value storages.
*/
func (tp *TariffPlan) restore(input string) {
elements := strings.Split(input, ";")
tp.SmsCredit, _ = strconv.ParseFloat(elements[0], 64)
tp.Traffic, _ = strconv.ParseFloat(elements[1], 64)
tp.ReceivedCallSecondsLimit, _ = strconv.ParseFloat(elements[2], 64)
tp.RecivedCallBonus = &RecivedCallBonus{}
tp.RecivedCallBonus.restore(elements[3])
for _, mbs := range strings.Split(elements[4], ",") {
mb := &MinuteBucket{}
mb.restore(mbs)
tp.MinuteBuckets = append(tp.MinuteBuckets, mb)
}
if len(elements) > 5 {
for _, vdss := range strings.Split(elements[5], ",") {
vd := &VolumeDiscount{}
vds := strings.Split(vdss, "|")
vd.Volume, _ = strconv.ParseFloat(vds[0], 64)
vd.Discount, _ = strconv.ParseFloat(vds[1], 64)
tp.VolumeDiscountThresholds = append(tp.VolumeDiscountThresholds, vd)
}
}
}
/*
Structure that holds the thresholds and for which
*/
@@ -52,3 +110,31 @@ type RecivedCallBonus struct {
Traffic float64
MinuteBucket *MinuteBucket
}
/*
Serializes the tariff plan for the storage. Used for key-value storages.
*/
func (rcb *RecivedCallBonus) store() (result string) {
result += strconv.FormatFloat(rcb.Credit, 'f', -1, 64) + ","
result += strconv.FormatFloat(rcb.SmsCredit, 'f', -1, 64) + ","
result += strconv.FormatFloat(rcb.Traffic, 'f', -1, 64)
if rcb.MinuteBucket != nil {
result += ","
result += rcb.MinuteBucket.store()
}
return
}
/*
De-serializes the tariff plan for the storage. Used for key-value storages.
*/
func (rcb *RecivedCallBonus) restore(input string) {
elements := strings.Split(input, ",")
rcb.Credit, _ = strconv.ParseFloat(elements[0], 64)
rcb.SmsCredit, _ = strconv.ParseFloat(elements[1], 64)
rcb.Traffic, _ = strconv.ParseFloat(elements[2], 64)
if len(elements) > 3 {
rcb.MinuteBucket = &MinuteBucket{}
rcb.MinuteBucket.restore(elements[3])
}
}

Binary file not shown.

View File

@@ -21,6 +21,8 @@ import (
// "log"
"sort"
"sync"
"strconv"
"strings"
)
/*
@@ -68,6 +70,50 @@ func (bs bucketsorter) Less(j, i int) bool {
bs[i].Price > bs[j].Price
}
/*
Serializes the user budget for the storage. Used for key-value storages.
*/
func (ub *UserBudget) store() (result string) {
result += strconv.FormatFloat(ub.Credit, 'f', -1, 64) + ";"
result += strconv.FormatFloat(ub.SmsCredit, 'f', -1, 64) + ";"
result += strconv.FormatFloat(ub.Traffic, 'f', -1, 64) + ";"
result += strconv.FormatFloat(ub.VolumeDiscountSeconds, 'f', -1, 64) + ";"
result += strconv.FormatFloat(ub.ReceivedCallSeconds, 'f', -1, 64) + ";"
result += strconv.Itoa(ub.ResetDayOfTheMonth) + ";"
result += ub.TariffPlanId
if ub.MinuteBuckets != nil {
result += ";"
}
for i, mb := range ub.MinuteBuckets {
if i > 0 {
result += ","
}
result += mb.store()
}
return
}
/*
De-serializes the user budget for the storage. Used for key-value storages.
*/
func (ub *UserBudget) restore(input string) {
elements := strings.Split(input, ";")
ub.Credit, _ = strconv.ParseFloat(elements[0], 64)
ub.SmsCredit, _ = strconv.ParseFloat(elements[1], 64)
ub.Traffic, _ = strconv.ParseFloat(elements[2], 64)
ub.VolumeDiscountSeconds, _ = strconv.ParseFloat(elements[3], 64)
ub.ReceivedCallSeconds, _ = strconv.ParseFloat(elements[4], 64)
ub.ResetDayOfTheMonth, _ = strconv.Atoi(elements[5])
ub.TariffPlanId = elements[6]
if len(elements) > 7 {
for _, mbs := range strings.Split(elements[7], ",") {
mb := &MinuteBucket{}
mb.restore(mbs)
ub.MinuteBuckets = append(ub.MinuteBuckets, mb)
}
}
}
/*
Returns the tariff plan loading it from the storage if necessary.
*/