github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/m3ninx/postings/roaring/roaring.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package roaring
    22  
    23  import (
    24  	"errors"
    25  	"fmt"
    26  
    27  	"github.com/m3db/m3/src/m3ninx/postings"
    28  	"github.com/m3db/m3/src/m3ninx/x"
    29  
    30  	"github.com/m3dbx/pilosa/roaring"
    31  )
    32  
    33  var (
    34  	errIntersectRoaringOnly  = errors.New("Intersect only supported between roaringDocId sets")
    35  	errUnionRoaringOnly      = errors.New("UnionInPlace only supported between roaringDocId sets")
    36  	errDifferenceRoaringOnly = errors.New("Difference only supported between roaringDocId sets")
    37  	errIteratorClosed        = errors.New("iterator has been closed")
    38  )
    39  
    40  // Union retrieves a new postings list which is the union of the provided lists.
    41  func Union(inputs []postings.List) (postings.MutableList, error) {
    42  	if len(inputs) == 0 {
    43  		return NewPostingsList(), nil
    44  	}
    45  
    46  	unioned := roaring.NewBitmap()
    47  	if err := union(unioned, inputs); err != nil {
    48  		return nil, err
    49  	}
    50  	return NewPostingsListFromBitmap(unioned), nil
    51  }
    52  
    53  func union(unionedBitmap *roaring.Bitmap, inputs []postings.List) error {
    54  	bitmaps := make([]*roaring.Bitmap, 0, len(inputs))
    55  	for _, in := range inputs {
    56  		pl, ok := in.(*postingsList)
    57  		if !ok {
    58  			return fmt.Errorf("unable to convert inputs into roaring postings lists")
    59  		}
    60  		bitmaps = append(bitmaps, pl.bitmap)
    61  	}
    62  
    63  	unionedBitmap.UnionInPlace(bitmaps...)
    64  	return nil
    65  }
    66  
    67  // BitmapFromPostingsList returns a bitmap from a postings list if it
    68  // is a roaring bitmap postings list.
    69  func BitmapFromPostingsList(pl postings.List) (*roaring.Bitmap, bool) {
    70  	result, ok := pl.(*postingsList)
    71  	if !ok {
    72  		return nil, false
    73  	}
    74  	return result.bitmap, true
    75  }
    76  
    77  // postingsList abstracts a Roaring Bitmap.
    78  type postingsList struct {
    79  	bitmap *roaring.Bitmap
    80  }
    81  
    82  // NewPostingsList returns a new mutable postings list backed by a Roaring Bitmap.
    83  func NewPostingsList() postings.MutableList {
    84  	return &postingsList{
    85  		bitmap: roaring.NewBitmap(),
    86  	}
    87  }
    88  
    89  // NewPostingsListFromBitmap returns a new mutable postings list using an
    90  // existing roaring bitmap.
    91  func NewPostingsListFromBitmap(bitmap *roaring.Bitmap) postings.MutableList {
    92  	return &postingsList{bitmap: bitmap}
    93  }
    94  
    95  func (d *postingsList) Insert(i postings.ID) error {
    96  	_ = d.bitmap.DirectAdd(uint64(i))
    97  	return nil
    98  }
    99  
   100  func (d *postingsList) Intersect(other postings.List) (postings.List, error) {
   101  	o, ok := other.(*postingsList)
   102  	if !ok {
   103  		return nil, errIntersectRoaringOnly
   104  	}
   105  
   106  	intersection := d.bitmap.Intersect(o.bitmap)
   107  
   108  	return NewPostingsListFromBitmap(intersection), nil
   109  }
   110  
   111  func (d *postingsList) Difference(other postings.List) (postings.List, error) {
   112  	o, ok := other.(*postingsList)
   113  	if !ok {
   114  		return nil, errDifferenceRoaringOnly
   115  	}
   116  
   117  	difference := d.bitmap.Difference(o.bitmap)
   118  
   119  	return NewPostingsListFromBitmap(difference), nil
   120  }
   121  
   122  func (d *postingsList) UnionInPlace(other postings.List) error {
   123  	o, ok := other.(*postingsList)
   124  	if !ok {
   125  		return errUnionRoaringOnly
   126  	}
   127  
   128  	d.bitmap.UnionInPlace(o.bitmap)
   129  	return nil
   130  }
   131  
   132  func (d *postingsList) UnionManyInPlace(others []postings.List) error {
   133  	return union(d.bitmap, others)
   134  }
   135  
   136  func (d *postingsList) AddRange(min, max postings.ID) error {
   137  	for i := min; i < max; i++ {
   138  		_, err := d.bitmap.Add(uint64(i))
   139  		if err != nil {
   140  			return err
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  func (d *postingsList) AddIterator(iter postings.Iterator) error {
   147  	safeIter := x.NewSafeCloser(iter)
   148  	defer safeIter.Close()
   149  
   150  	for iter.Next() {
   151  		if err := d.Insert(iter.Current()); err != nil {
   152  			return err
   153  		}
   154  	}
   155  
   156  	if err := iter.Err(); err != nil {
   157  		return err
   158  	}
   159  
   160  	return safeIter.Close()
   161  }
   162  
   163  func (d *postingsList) RemoveRange(min, max postings.ID) error {
   164  	for i := min; i < max; i++ {
   165  		_, err := d.bitmap.Remove(uint64(i))
   166  		if err != nil {
   167  			return err
   168  		}
   169  	}
   170  	return nil
   171  }
   172  
   173  func (d *postingsList) Reset() {
   174  	d.bitmap.Reset()
   175  }
   176  
   177  func (d *postingsList) Contains(i postings.ID) bool {
   178  	return d.bitmap.Contains(uint64(i))
   179  }
   180  
   181  func (d *postingsList) IsEmpty() bool {
   182  	return d.bitmap.Count() == 0
   183  }
   184  
   185  func (d *postingsList) Max() (postings.ID, error) {
   186  	if d.IsEmpty() {
   187  		return 0, postings.ErrEmptyList
   188  	}
   189  	max := d.bitmap.Max()
   190  	return postings.ID(max), nil
   191  }
   192  
   193  func (d *postingsList) Len() int {
   194  	return int(d.bitmap.Count())
   195  }
   196  
   197  func (d *postingsList) Iterator() postings.Iterator {
   198  	return &roaringIterator{
   199  		iter: d.bitmap.Iterator(),
   200  	}
   201  }
   202  
   203  func (d *postingsList) CloneAsMutable() postings.MutableList {
   204  	// TODO: It's cheaper to Clone than to cache roaring bitmaps, see
   205  	// `postings_list_bench_test.go`. Their internals don't allow for
   206  	// pooling at the moment. We should address this when get a chance
   207  	// (move to another implementation / address deficiencies).
   208  	return &postingsList{
   209  		bitmap: d.bitmap.Clone(),
   210  	}
   211  }
   212  
   213  func (d *postingsList) Equal(other postings.List) bool {
   214  	if d.Len() != other.Len() {
   215  		return false
   216  	}
   217  
   218  	iter := d.Iterator()
   219  	otherIter := other.Iterator()
   220  
   221  	for iter.Next() {
   222  		if !otherIter.Next() {
   223  			return false
   224  		}
   225  		if iter.Current() != otherIter.Current() {
   226  			return false
   227  		}
   228  	}
   229  
   230  	return true
   231  }
   232  
   233  type roaringIterator struct {
   234  	iter    *roaring.Iterator
   235  	current postings.ID
   236  	closed  bool
   237  }
   238  
   239  func (it *roaringIterator) Current() postings.ID {
   240  	return it.current
   241  }
   242  
   243  func (it *roaringIterator) Next() bool {
   244  	if it.closed {
   245  		return false
   246  	}
   247  	v, eof := it.iter.Next()
   248  	if eof {
   249  		return false
   250  	}
   251  	it.current = postings.ID(v)
   252  	return true
   253  }
   254  
   255  func (it *roaringIterator) Err() error {
   256  	return nil
   257  }
   258  
   259  func (it *roaringIterator) Close() error {
   260  	if it.closed {
   261  		return errIteratorClosed
   262  	}
   263  	it.closed = true
   264  	return nil
   265  }