github.com/susy-go/susy-graviton@v0.0.0-20190614130430-36cddae42305/swarm/shed/field_uint64.go (about)

     1  // Copyleft 2018 The susy-graviton Authors
     2  // This file is part of the susy-graviton library.
     3  //
     4  // The susy-graviton library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The susy-graviton library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MSRCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the susy-graviton library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package shed
    18  
    19  import (
    20  	"encoding/binary"
    21  
    22  	"github.com/syndtr/goleveldb/leveldb"
    23  )
    24  
    25  // Uint64Field provides a way to have a simple counter in the database.
    26  // It transparently encodes uint64 type value to bytes.
    27  type Uint64Field struct {
    28  	db  *DB
    29  	key []byte
    30  }
    31  
    32  // NewUint64Field returns a new Uint64Field.
    33  // It validates its name and type against the database schema.
    34  func (db *DB) NewUint64Field(name string) (f Uint64Field, err error) {
    35  	key, err := db.schemaFieldKey(name, "uint64")
    36  	if err != nil {
    37  		return f, err
    38  	}
    39  	return Uint64Field{
    40  		db:  db,
    41  		key: key,
    42  	}, nil
    43  }
    44  
    45  // Get retrieves a uint64 value from the database.
    46  // If the value is not found in the database a 0 value
    47  // is returned and no error.
    48  func (f Uint64Field) Get() (val uint64, err error) {
    49  	b, err := f.db.Get(f.key)
    50  	if err != nil {
    51  		if err == leveldb.ErrNotFound {
    52  			return 0, nil
    53  		}
    54  		return 0, err
    55  	}
    56  	return binary.BigEndian.Uint64(b), nil
    57  }
    58  
    59  // Put encodes uin64 value and stores it in the database.
    60  func (f Uint64Field) Put(val uint64) (err error) {
    61  	return f.db.Put(f.key, encodeUint64(val))
    62  }
    63  
    64  // PutInBatch stores a uint64 value in a batch
    65  // that can be saved later in the database.
    66  func (f Uint64Field) PutInBatch(batch *leveldb.Batch, val uint64) {
    67  	batch.Put(f.key, encodeUint64(val))
    68  }
    69  
    70  // Inc increments a uint64 value in the database.
    71  // This operation is not goroutine save.
    72  func (f Uint64Field) Inc() (val uint64, err error) {
    73  	val, err = f.Get()
    74  	if err != nil {
    75  		if err == leveldb.ErrNotFound {
    76  			val = 0
    77  		} else {
    78  			return 0, err
    79  		}
    80  	}
    81  	val++
    82  	return val, f.Put(val)
    83  }
    84  
    85  // IncInBatch increments a uint64 value in the batch
    86  // by retreiving a value from the database, not the same batch.
    87  // This operation is not goroutine save.
    88  func (f Uint64Field) IncInBatch(batch *leveldb.Batch) (val uint64, err error) {
    89  	val, err = f.Get()
    90  	if err != nil {
    91  		if err == leveldb.ErrNotFound {
    92  			val = 0
    93  		} else {
    94  			return 0, err
    95  		}
    96  	}
    97  	val++
    98  	f.PutInBatch(batch, val)
    99  	return val, nil
   100  }
   101  
   102  // Dec decrements a uint64 value in the database.
   103  // This operation is not goroutine save.
   104  // The field is protected from overflow to a negative value.
   105  func (f Uint64Field) Dec() (val uint64, err error) {
   106  	val, err = f.Get()
   107  	if err != nil {
   108  		if err == leveldb.ErrNotFound {
   109  			val = 0
   110  		} else {
   111  			return 0, err
   112  		}
   113  	}
   114  	if val != 0 {
   115  		val--
   116  	}
   117  	return val, f.Put(val)
   118  }
   119  
   120  // DecInBatch decrements a uint64 value in the batch
   121  // by retreiving a value from the database, not the same batch.
   122  // This operation is not goroutine save.
   123  // The field is protected from overflow to a negative value.
   124  func (f Uint64Field) DecInBatch(batch *leveldb.Batch) (val uint64, err error) {
   125  	val, err = f.Get()
   126  	if err != nil {
   127  		if err == leveldb.ErrNotFound {
   128  			val = 0
   129  		} else {
   130  			return 0, err
   131  		}
   132  	}
   133  	if val != 0 {
   134  		val--
   135  	}
   136  	f.PutInBatch(batch, val)
   137  	return val, nil
   138  }
   139  
   140  // encode transforms uint64 to 8 byte long
   141  // slice in big endian encoding.
   142  func encodeUint64(val uint64) (b []byte) {
   143  	b = make([]byte, 8)
   144  	binary.BigEndian.PutUint64(b, val)
   145  	return b
   146  }