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 }