github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/storage/dimension/dimension.go (about)

     1  package dimension
     2  
     3  import (
     4  	"bytes"
     5  	"sort"
     6  	"sync"
     7  )
     8  
     9  type Key []byte
    10  
    11  type Dimension struct {
    12  	m sync.RWMutex
    13  	// keys are sorted
    14  	Keys []Key
    15  }
    16  
    17  func New() *Dimension {
    18  	return &Dimension{
    19  		Keys: []Key{},
    20  	}
    21  }
    22  
    23  func (d *Dimension) Insert(key Key) {
    24  	d.m.Lock()
    25  	defer d.m.Unlock()
    26  
    27  	i := sort.Search(len(d.Keys), func(i int) bool {
    28  		return bytes.Compare(d.Keys[i], key) >= 0
    29  	})
    30  
    31  	if i < len(d.Keys) && bytes.Equal(d.Keys[i], key) {
    32  		return
    33  	}
    34  
    35  	if i > len(d.Keys)-1 || !bytes.Equal(d.Keys[i], key) {
    36  		d.Keys = append(d.Keys, key)
    37  		copy(d.Keys[i+1:], d.Keys[i:])
    38  		d.Keys[i] = key
    39  	}
    40  }
    41  
    42  func (d *Dimension) Delete(key Key) {
    43  	d.m.Lock()
    44  	defer d.m.Unlock()
    45  
    46  	i := sort.Search(len(d.Keys), func(i int) bool {
    47  		return bytes.Compare(d.Keys[i], key) >= 0
    48  	})
    49  
    50  	if i < len(d.Keys) && bytes.Equal(d.Keys[i], key) {
    51  		d.Keys = append(d.Keys[:i], d.Keys[i+1:]...)
    52  		return
    53  	}
    54  }
    55  
    56  type advanceResult int
    57  
    58  const (
    59  	match advanceResult = iota
    60  	noMatch
    61  	end
    62  )
    63  
    64  type sortableDim struct {
    65  	keys []Key
    66  	i    int
    67  	l    int
    68  }
    69  
    70  func (sd *sortableDim) current() Key {
    71  	return sd.keys[sd.i]
    72  }
    73  
    74  func (sd *sortableDim) advance(cmp Key) advanceResult {
    75  	for {
    76  		v := bytes.Compare(sd.current(), cmp)
    77  		switch v {
    78  		case 0:
    79  			return match
    80  		case 1:
    81  			return noMatch
    82  		}
    83  		sd.i++
    84  		if sd.i == sd.l {
    85  			return end
    86  		}
    87  	}
    88  }
    89  
    90  type sortableDims []*sortableDim
    91  
    92  func (s sortableDims) Len() int {
    93  	return len(s)
    94  }
    95  
    96  func (s sortableDims) Less(i, j int) bool {
    97  	return bytes.Compare(s[i].current(), s[j].current()) >= 0
    98  }
    99  
   100  func (s sortableDims) Swap(i, j int) {
   101  	s[i], s[j] = s[j], s[i]
   102  }
   103  
   104  // finds keys that are present in all dimensions
   105  func Intersection(input ...*Dimension) []Key {
   106  	if len(input) == 0 {
   107  		return []Key{}
   108  	} else if len(input) == 1 {
   109  		return input[0].Keys
   110  	}
   111  
   112  	// TODO(kolesnikov): shouldn't a bitmap be used?
   113  	r := make(map[string]int)
   114  	for _, v := range input {
   115  		for _, k := range v.Keys {
   116  			r[string(k)]++
   117  		}
   118  	}
   119  
   120  	var res []Key
   121  	for k, v := range r {
   122  		if v == len(input) {
   123  			res = append(res, []byte(k))
   124  		}
   125  	}
   126  
   127  	return res
   128  }
   129  
   130  // TODO: we need to take advantage of the fact that these are sorted arrays
   131  // Current implementation might be taking too much memory
   132  func Union(input ...*Dimension) []Key {
   133  	if len(input) == 0 {
   134  		return []Key{}
   135  	} else if len(input) == 1 {
   136  		return input[0].Keys
   137  	}
   138  
   139  	result := []Key{}
   140  
   141  	isExists := map[string]bool{}
   142  
   143  	for _, v := range input {
   144  		for _, k := range v.Keys {
   145  			if !isExists[string(k)] {
   146  				result = append(result, k)
   147  			}
   148  
   149  			isExists[string(k)] = true
   150  		}
   151  	}
   152  
   153  	return result
   154  }
   155  
   156  // TODO: rework
   157  func AndNot(a, b *Dimension) []Key {
   158  	a.m.RLock()
   159  	defer a.m.RUnlock()
   160  	if len(a.Keys) == 0 {
   161  		return nil
   162  	}
   163  
   164  	b.m.RLock()
   165  	defer b.m.RUnlock()
   166  	if len(b.Keys) == 0 {
   167  		r := make([]Key, len(a.Keys))
   168  		copy(r, a.Keys)
   169  		return r
   170  	}
   171  
   172  	r := make([]Key, 0, len(a.Keys))
   173  	m := make(map[string]struct{}, len(b.Keys))
   174  
   175  	for _, k := range b.Keys {
   176  		m[string(k)] = struct{}{}
   177  	}
   178  
   179  	for _, k := range a.Keys {
   180  		if _, ok := m[string(k)]; !ok {
   181  			r = append(r, k)
   182  		}
   183  	}
   184  
   185  	return r
   186  }