diff --git a/swarm/shed/field_uint64.go b/swarm/shed/field_uint64.go index 80e0069ae4..0417583ac3 100644 --- a/swarm/shed/field_uint64.go +++ b/swarm/shed/field_uint64.go @@ -99,6 +99,44 @@ func (f Uint64Field) IncInBatch(batch *leveldb.Batch) (val uint64, err error) { return val, nil } +// Dec decrements a uint64 value in the database. +// This operation is not goroutine save. +// The field is protected from overflow to a negative value. +func (f Uint64Field) Dec() (val uint64, err error) { + val, err = f.Get() + if err != nil { + if err == leveldb.ErrNotFound { + val = 0 + } else { + return 0, err + } + } + if val != 0 { + val-- + } + return val, f.Put(val) +} + +// DecInBatch decrements a uint64 value in the batch +// by retreiving a value from the database, not the same batch. +// This operation is not goroutine save. +// The field is protected from overflow to a negative value. +func (f Uint64Field) DecInBatch(batch *leveldb.Batch) (val uint64, err error) { + val, err = f.Get() + if err != nil { + if err == leveldb.ErrNotFound { + val = 0 + } else { + return 0, err + } + } + if val != 0 { + val-- + } + f.PutInBatch(batch, val) + return val, nil +} + // encode transforms uint64 to 8 byte long // slice in big endian encoding. func encodeUint64(val uint64) (b []byte) { diff --git a/swarm/shed/field_uint64_test.go b/swarm/shed/field_uint64_test.go index 69ade71ba3..9462b56dd1 100644 --- a/swarm/shed/field_uint64_test.go +++ b/swarm/shed/field_uint64_test.go @@ -192,3 +192,109 @@ func TestUint64Field_IncInBatch(t *testing.T) { t.Errorf("got uint64 %v, want %v", got, want) } } + +// TestUint64Field_Dec validates Dec operation +// of the Uint64Field. +func TestUint64Field_Dec(t *testing.T) { + db, cleanupFunc := newTestDB(t) + defer cleanupFunc() + + counter, err := db.NewUint64Field("counter") + if err != nil { + t.Fatal(err) + } + + // test overflow protection + var want uint64 + got, err := counter.Dec() + if err != nil { + t.Fatal(err) + } + if got != want { + t.Errorf("got uint64 %v, want %v", got, want) + } + + want = 32 + err = counter.Put(want) + if err != nil { + t.Fatal(err) + } + + want = 31 + got, err = counter.Dec() + if err != nil { + t.Fatal(err) + } + if got != want { + t.Errorf("got uint64 %v, want %v", got, want) + } +} + +// TestUint64Field_DecInBatch validates DecInBatch operation +// of the Uint64Field. +func TestUint64Field_DecInBatch(t *testing.T) { + db, cleanupFunc := newTestDB(t) + defer cleanupFunc() + + counter, err := db.NewUint64Field("counter") + if err != nil { + t.Fatal(err) + } + + batch := new(leveldb.Batch) + var want uint64 + got, err := counter.DecInBatch(batch) + if err != nil { + t.Fatal(err) + } + if got != want { + t.Errorf("got uint64 %v, want %v", got, want) + } + err = db.WriteBatch(batch) + if err != nil { + t.Fatal(err) + } + got, err = counter.Get() + if err != nil { + t.Fatal(err) + } + if got != want { + t.Errorf("got uint64 %v, want %v", got, want) + } + + batch2 := new(leveldb.Batch) + want = 42 + counter.PutInBatch(batch2, want) + err = db.WriteBatch(batch2) + if err != nil { + t.Fatal(err) + } + got, err = counter.Get() + if err != nil { + t.Fatal(err) + } + if got != want { + t.Errorf("got uint64 %v, want %v", got, want) + } + + batch3 := new(leveldb.Batch) + want = 41 + got, err = counter.DecInBatch(batch3) + if err != nil { + t.Fatal(err) + } + if got != want { + t.Errorf("got uint64 %v, want %v", got, want) + } + err = db.WriteBatch(batch3) + if err != nil { + t.Fatal(err) + } + got, err = counter.Get() + if err != nil { + t.Fatal(err) + } + if got != want { + t.Errorf("got uint64 %v, want %v", got, want) + } +}