github.com/insolar/vanilla@v0.0.0-20201023172447-248fdf805322/cachekit/alloc.go (about)

     1  // Copyright 2020 Insolar Network Ltd.
     2  // All rights reserved.
     3  // This material is licensed under the Insolar License version 1.0,
     4  // available at https://github.com/insolar/assured-ledger/blob/master/LICENSE.md.
     5  
     6  package cachekit
     7  
     8  import (
     9  	"math"
    10  
    11  	"github.com/insolar/vanilla/throw"
    12  )
    13  
    14  func newAllocationTracker(pageSize int) allocationTracker {
    15  	if pageSize <= 0 {
    16  		panic(throw.IllegalValue())
    17  	}
    18  	return allocationTracker{
    19  		pages:   []allocPage{ make(allocPage, 0, pageSize) },
    20  		freeIdx: math.MinInt32,
    21  	}
    22  }
    23  
    24  type allocationTracker struct {
    25  	pages   []allocPage
    26  	freeIdx int32
    27  	freeCnt uint32
    28  }
    29  
    30  type allocPage []int32
    31  
    32  func (p *allocationTracker) Add(value int32) int {
    33  	switch {
    34  	case value < 0:
    35  		panic(throw.IllegalValue())
    36  	case p.freeIdx != math.MinInt32:
    37  		return p.reuse(value)
    38  	}
    39  
    40  	pgN := len(p.pages) - 1
    41  	pg := &p.pages[pgN]
    42  	pgSize := cap(*pg)
    43  	n := len(*pg)
    44  
    45  	if n == pgSize {
    46  		p.pages = append(p.pages, make(allocPage, 0, pgSize))
    47  		pgN++
    48  		n = 0
    49  		pg = &p.pages[pgN]
    50  	}
    51  
    52  	*pg = append(*pg, value)
    53  	return pgN * pgSize + n
    54  }
    55  
    56  func (p *allocationTracker) get(index int) *int32 {
    57  	pgSize := cap(p.pages[0])
    58  	pgN := index / pgSize
    59  	if pgN >= len(p.pages) {
    60  		return nil
    61  	}
    62  	n := index % pgSize
    63  	if n >= len(p.pages[pgN]) {
    64  		return nil
    65  	}
    66  	return &p.pages[pgN][n]
    67  }
    68  
    69  
    70  func (p *allocationTracker) AllocatedCount() int {
    71  	n := len(p.pages)
    72  	if n == 0 {
    73  		return 0
    74  	}
    75  	return n * cap(p.pages[0])
    76  }
    77  
    78  func (p *allocationTracker) Count() int {
    79  	pgN := len(p.pages)
    80  	if pgN == 0 {
    81  		return 0
    82  	}
    83  
    84  	pgSize := cap(p.pages[0])
    85  	pgN--
    86  
    87  	return pgN * pgSize + len(p.pages[pgN]) - int(p.freeCnt)
    88  }
    89  
    90  func (p *allocationTracker) Get(index int) (int32, bool) {
    91  	switch vi := p.get(index); {
    92  	case vi == nil:
    93  		return -1, false
    94  	case *vi < 0:
    95  		return -1, false
    96  	default:
    97  		return *vi, true
    98  	}
    99  }
   100  
   101  func (p *allocationTracker) Set(index int, value int32) bool {
   102  	switch vi := p.get(index); {
   103  	case value < 0:
   104  		panic(throw.IllegalValue())
   105  	case vi == nil:
   106  		return false
   107  	case *vi < 0:
   108  		return false
   109  	default:
   110  		*vi = value
   111  		return true
   112  	}
   113  }
   114  
   115  func (p *allocationTracker) Inc(index int) (int32, bool, bool) {
   116  	switch vi := p.get(index); {
   117  	case vi == nil:
   118  		return -1, false, false
   119  	case *vi < 0:
   120  		return -1, false, false
   121  	default:
   122  		switch n := *vi; {
   123  		case n < 0:
   124  			panic(throw.IllegalValue())
   125  		case n == math.MaxInt32:
   126  			return math.MaxInt32, true, true
   127  		default:
   128  			n++
   129  			*vi = n
   130  			return n, false, true
   131  		}
   132  	}
   133  }
   134  
   135  func (p *allocationTracker) Dec(index int) (int32, bool) {
   136  	switch vi := p.get(index); {
   137  	case vi == nil:
   138  		return -1, false
   139  	case *vi < 0:
   140  		return -1, false
   141  	default:
   142  		switch n := *vi; {
   143  		case n < 0:
   144  			panic(throw.IllegalValue())
   145  		case n == 0:
   146  			return 0, true
   147  		default:
   148  			n--
   149  			*vi = n
   150  			return n, true
   151  		}
   152  	}
   153  }
   154  
   155  func (p *allocationTracker) Delete(index int) bool {
   156  	switch vi := p.get(index); {
   157  	case vi == nil:
   158  		return false
   159  	case *vi < 0:
   160  		return false
   161  	case p.freeIdx >= 0:
   162  		panic(throw.Impossible())
   163  	default:
   164  		*vi = p.freeIdx
   165  		p.freeIdx = -int32(index + 1)
   166  		p.freeCnt++
   167  		return true
   168  	}
   169  }
   170  
   171  func (p *allocationTracker) reuse(value int32) int {
   172  	free := p.freeIdx
   173  	switch {
   174  	case free >= 0:
   175  		panic(throw.IllegalState())
   176  	case free == math.MinInt32:
   177  		// this method can't be called then
   178  		panic(throw.Impossible())
   179  	case p.freeCnt == 0:
   180  		panic(throw.Impossible())
   181  	}
   182  
   183  	n := -int(free)-1
   184  	switch vi := p.get(n); {
   185  	case vi == nil:
   186  		panic(throw.Impossible())
   187  	case *vi >= 0:
   188  		panic(throw.Impossible())
   189  	default:
   190  		p.freeIdx = *vi
   191  		p.freeCnt--
   192  		*vi = value
   193  	}
   194  	return n
   195  }