Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0
Ce dépôt a été archivé le 2022-05-07. Vous pouvez voir ses fichiers ou le cloner, mais pas ouvrir de ticket ou de demandes d'ajout, ni soumettre de changements.
nyaa-pantsu/vendor/github.com/RoaringBitmap/roaring/roaring.go
akuma06 b2b48f61b0 Torrent Generation on not found error (#1600)
* [WIP] Torrent Generation on not found error
As asked in #1517, it allows on-the-fly torrent generation. Since it uses magnet links, it needs some time to connect to peers. So it can't be instant generation, we need the user to wait and try after a minute at least.

* Replace Fatal by simple error

* attempt at fixing travis

* del

* Add Anacrolyx dependency

* Add back difflib

* Remove .torrent suffix in the url example

* Add some explanations when file missing page shown

* Ignore downloads directory

* Either use cache (third-party site) or own download directory

* Wrong import

* If there is an error then it means we aren't generating a torrent file

May it be "torrent not found" or "We do not store torrent files" which are the two only existing errors for this page

* hash is never empty

* TorrentLink may be empty at times

So we add a /download/:hash link if it is

* Update README.md

* Made a mistake here, need to check if false

* Update en-us.all.json

* Update CHANGELOG.md

* Torrent file generation can be triggered by click on button if JS enabled

* Update download.go

* Update download.go

* Use c.JSON instead of text/template

* Return to default behavior if we don't generate the file

* Don't do the query if returned to default behavior

* Add "Could not generate torrent file" error

* Fix JS condition & lower delay until button updates

* Start download automatically once torrent file is generated

* Fix torrentFileExists() constantly returning false if external torrent download URL

* torrent-view-data is two tables instead of one

This allows the removal of useless things without any problem (e.g Website link), but also a better responsibe design since the previous one separated stats after a certain res looking very wonky

* CSS changes to go along

* Remove useless <b></b>

* Update main.css

* In torrentFileExists, check if filestorage path exists instead of looking at the domain in torrent link

When checking if the file is stored on another server i used to simply check if the domain name was inside the torrent link, but we can straight up check for filestorage length

* Fix JS of on-demand stat fetching

* ScrapeAge variable accessible through view.jet.html

Contains last scraped time in hours, is at -1 is torrent has never been scraped
Stats will get updated if it's either at -1 or above 1460 (2 months old)

* Refresh stats if older than two months OR unknown and older than 24h

Show last scraped date even if stats are unknown

* Add StatsObsolete variable to torrent

Indicating if:
- They can be shown
- They need to be updated

* Update scraped data even if Unknown, prevent users from trying to fetch stats every seconds

* Torrent file stored locally by default

* no need to do all of that if no filestorage

* fix filestorage path

* Fix torrent download button stuck on "Generating torrent file" at rare times

* fix some css rules that didn't work on IE

* Fix panic error

Seems like this error is a known bug from  anacrolyx torrent https://github.com/anacrolix/torrent/issues/83

To prevent it, I'm creating a single client and modifying the socket.go to make it not raise a panic but a simple error log.
2017-10-21 09:40:43 +02:00

1221 lignes
36 Kio
Go

// Package roaring is an implementation of Roaring Bitmaps in Go.
// They provide fast compressed bitmap data structures (also called bitset).
// They are ideally suited to represent sets of integers over
// relatively small ranges.
// See http://roaringbitmap.org for details.
package roaring
import (
"bufio"
"bytes"
"encoding/base64"
"fmt"
"io"
"strconv"
)
// Bitmap represents a compressed bitmap where you can add integers.
type Bitmap struct {
highlowcontainer roaringArray
}
// ToBase64 serializes a bitmap as Base64
func (rb *Bitmap) ToBase64() (string, error) {
buf := new(bytes.Buffer)
_, err := rb.WriteTo(buf)
return base64.StdEncoding.EncodeToString(buf.Bytes()), err
}
// FromBase64 deserializes a bitmap from Base64
func (rb *Bitmap) FromBase64(str string) (int64, error) {
data, err := base64.StdEncoding.DecodeString(str)
if err != nil {
return 0, err
}
buf := bytes.NewBuffer(data)
return rb.ReadFrom(buf)
}
// WriteTo writes a serialized version of this bitmap to stream.
// The format is compatible with other RoaringBitmap
// implementations (Java, C) and is documented here:
// https://github.com/RoaringBitmap/RoaringFormatSpec
func (rb *Bitmap) WriteTo(stream io.Writer) (int64, error) {
return rb.highlowcontainer.writeTo(stream)
}
// ToBytes returns an array of bytes corresponding to what is written
// when calling WriteTo
func (rb *Bitmap) ToBytes() ([]byte, error) {
return rb.highlowcontainer.toBytes()
}
// WriteToMsgpack writes a msgpack2/snappy-streaming compressed serialized
// version of this bitmap to stream. The format is not
// compatible with the WriteTo() format, and is
// experimental: it may produce smaller on disk
// footprint and/or be faster to read, depending
// on your content. Currently only the Go roaring
// implementation supports this format.
func (rb *Bitmap) WriteToMsgpack(stream io.Writer) (int64, error) {
return 0, rb.highlowcontainer.writeToMsgpack(stream)
}
// ReadFrom reads a serialized version of this bitmap from stream.
// The format is compatible with other RoaringBitmap
// implementations (Java, C) and is documented here:
// https://github.com/RoaringBitmap/RoaringFormatSpec
func (rb *Bitmap) ReadFrom(stream io.Reader) (int64, error) {
return rb.highlowcontainer.readFrom(stream)
}
// RunOptimize attempts to further compress the runs of consecutive values found in the bitmap
func (rb *Bitmap) RunOptimize() {
rb.highlowcontainer.runOptimize()
}
// HasRunCompression returns true if the bitmap benefits from run compression
func (rb *Bitmap) HasRunCompression() bool {
return rb.highlowcontainer.hasRunCompression()
}
// ReadFromMsgpack reads a msgpack2/snappy-streaming serialized
// version of this bitmap from stream. The format is
// expected is that written by the WriteToMsgpack()
// call; see additional notes there.
func (rb *Bitmap) ReadFromMsgpack(stream io.Reader) (int64, error) {
return 0, rb.highlowcontainer.readFromMsgpack(stream)
}
// MarshalBinary implements the encoding.BinaryMarshaler interface for the bitmap
func (rb *Bitmap) MarshalBinary() ([]byte, error) {
var buf bytes.Buffer
writer := bufio.NewWriter(&buf)
_, err := rb.WriteTo(writer)
if err != nil {
return nil, err
}
err = writer.Flush()
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface for the bitmap
func (rb *Bitmap) UnmarshalBinary(data []byte) error {
var buf bytes.Buffer
_, err := buf.Write(data)
if err != nil {
return err
}
reader := bufio.NewReader(&buf)
_, err = rb.ReadFrom(reader)
return err
}
// NewBitmap creates a new empty Bitmap (see also New)
func NewBitmap() *Bitmap {
return &Bitmap{*newRoaringArray()}
}
// New creates a new empty Bitmap (same as NewBitmap)
func New() *Bitmap {
return &Bitmap{*newRoaringArray()}
}
// Clear removes all content from the Bitmap and frees the memory
func (rb *Bitmap) Clear() {
rb.highlowcontainer = *newRoaringArray()
}
// ToArray creates a new slice containing all of the integers stored in the Bitmap in sorted order
func (rb *Bitmap) ToArray() []uint32 {
array := make([]uint32, rb.GetCardinality())
pos := 0
pos2 := 0
for pos < rb.highlowcontainer.size() {
hs := uint32(rb.highlowcontainer.getKeyAtIndex(pos)) << 16
c := rb.highlowcontainer.getContainerAtIndex(pos)
pos++
c.fillLeastSignificant16bits(array, pos2, hs)
pos2 += c.getCardinality()
}
return array
}
// GetSizeInBytes estimates the memory usage of the Bitmap. Note that this
// might differ slightly from the amount of bytes required for persistent storage
func (rb *Bitmap) GetSizeInBytes() uint64 {
size := uint64(8)
for _, c := range rb.highlowcontainer.containers {
size += uint64(2) + uint64(c.getSizeInBytes())
}
return size
}
// GetSerializedSizeInBytes computes the serialized size in bytes
// of the Bitmap. It should correspond to the
// number of bytes written when invoking WriteTo. You can expect
// that this function is much cheaper computationally than WriteTo.
func (rb *Bitmap) GetSerializedSizeInBytes() uint64 {
return rb.highlowcontainer.serializedSizeInBytes()
}
// BoundSerializedSizeInBytes returns an upper bound on the serialized size in bytes
// assuming that one wants to store "cardinality" integers in [0, universe_size)
func BoundSerializedSizeInBytes(cardinality uint64, universeSize uint64) uint64 {
contnbr := (universeSize + uint64(65535)) / uint64(65536)
if contnbr > cardinality {
contnbr = cardinality
// we can't have more containers than we have values
}
headermax := 8*contnbr + 4
if 4 > (contnbr+7)/8 {
headermax += 4
} else {
headermax += (contnbr + 7) / 8
}
valsarray := uint64(arrayContainerSizeInBytes(int(cardinality)))
valsbitmap := contnbr * uint64(bitmapContainerSizeInBytes())
valsbest := valsarray
if valsbest > valsbitmap {
valsbest = valsbitmap
}
return valsbest + headermax
}
// IntIterable allows you to iterate over the values in a Bitmap
type IntIterable interface {
HasNext() bool
Next() uint32
}
type intIterator struct {
pos int
hs uint32
iter shortIterable
highlowcontainer *roaringArray
}
// HasNext returns true if there are more integers to iterate over
func (ii *intIterator) HasNext() bool {
return ii.pos < ii.highlowcontainer.size()
}
func (ii *intIterator) init() {
if ii.highlowcontainer.size() > ii.pos {
ii.iter = ii.highlowcontainer.getContainerAtIndex(ii.pos).getShortIterator()
ii.hs = uint32(ii.highlowcontainer.getKeyAtIndex(ii.pos)) << 16
}
}
// Next returns the next integer
func (ii *intIterator) Next() uint32 {
x := uint32(ii.iter.next()) | ii.hs
if !ii.iter.hasNext() {
ii.pos = ii.pos + 1
ii.init()
}
return x
}
func newIntIterator(a *Bitmap) *intIterator {
p := new(intIterator)
p.pos = 0
p.highlowcontainer = &a.highlowcontainer
p.init()
return p
}
// String creates a string representation of the Bitmap
func (rb *Bitmap) String() string {
// inspired by https://github.com/fzandona/goroar/
var buffer bytes.Buffer
start := []byte("{")
buffer.Write(start)
i := rb.Iterator()
counter := 0
if i.HasNext() {
counter = counter + 1
buffer.WriteString(strconv.FormatInt(int64(i.Next()), 10))
}
for i.HasNext() {
buffer.WriteString(",")
counter = counter + 1
// to avoid exhausting the memory
if counter > 0x40000 {
buffer.WriteString("...")
break
}
buffer.WriteString(strconv.FormatInt(int64(i.Next()), 10))
}
buffer.WriteString("}")
return buffer.String()
}
// Iterator creates a new IntIterable to iterate over the integers contained in the bitmap, in sorted order
func (rb *Bitmap) Iterator() IntIterable {
return newIntIterator(rb)
}
// Clone creates a copy of the Bitmap
func (rb *Bitmap) Clone() *Bitmap {
ptr := new(Bitmap)
ptr.highlowcontainer = *rb.highlowcontainer.clone()
return ptr
}
// Minimum get the smallest value stored in this roaring bitmap, assumes that it is not empty
func (rb *Bitmap) Minimum() uint32 {
return uint32(rb.highlowcontainer.containers[0].minimum()) | (uint32(rb.highlowcontainer.keys[0]) << 16)
}
// Maximum get the largest value stored in this roaring bitmap, assumes that it is not empty
func (rb *Bitmap) Maximum() uint32 {
lastindex := len(rb.highlowcontainer.containers) - 1
return uint32(rb.highlowcontainer.containers[lastindex].maximum()) | (uint32(rb.highlowcontainer.keys[lastindex]) << 16)
}
// Contains returns true if the integer is contained in the bitmap
func (rb *Bitmap) Contains(x uint32) bool {
hb := highbits(x)
c := rb.highlowcontainer.getContainer(hb)
return c != nil && c.contains(lowbits(x))
}
// ContainsInt returns true if the integer is contained in the bitmap (this is a convenience method, the parameter is casted to uint32 and Contains is called)
func (rb *Bitmap) ContainsInt(x int) bool {
return rb.Contains(uint32(x))
}
// Equals returns true if the two bitmaps contain the same integers
func (rb *Bitmap) Equals(o interface{}) bool {
srb, ok := o.(*Bitmap)
if ok {
return srb.highlowcontainer.equals(rb.highlowcontainer)
}
return false
}
// Add the integer x to the bitmap
func (rb *Bitmap) Add(x uint32) {
hb := highbits(x)
ra := &rb.highlowcontainer
i := ra.getIndex(hb)
if i >= 0 {
var c container
c = ra.getWritableContainerAtIndex(i).iaddReturnMinimized(lowbits(x))
rb.highlowcontainer.setContainerAtIndex(i, c)
} else {
newac := newArrayContainer()
rb.highlowcontainer.insertNewKeyValueAt(-i-1, hb, newac.iaddReturnMinimized(lowbits(x)))
}
}
// add the integer x to the bitmap, return the container and its index
func (rb *Bitmap) addwithptr(x uint32) (int, container) {
hb := highbits(x)
ra := &rb.highlowcontainer
i := ra.getIndex(hb)
var c container
if i >= 0 {
c = ra.getWritableContainerAtIndex(i).iaddReturnMinimized(lowbits(x))
rb.highlowcontainer.setContainerAtIndex(i, c)
return i, c
}
newac := newArrayContainer()
c = newac.iaddReturnMinimized(lowbits(x))
rb.highlowcontainer.insertNewKeyValueAt(-i-1, hb, c)
return -i - 1, c
}
// CheckedAdd adds the integer x to the bitmap and return true if it was added (false if the integer was already present)
func (rb *Bitmap) CheckedAdd(x uint32) bool {
// TODO: add unit tests for this method
hb := highbits(x)
i := rb.highlowcontainer.getIndex(hb)
if i >= 0 {
C := rb.highlowcontainer.getWritableContainerAtIndex(i)
oldcard := C.getCardinality()
C = C.iaddReturnMinimized(lowbits(x))
rb.highlowcontainer.setContainerAtIndex(i, C)
return C.getCardinality() > oldcard
}
newac := newArrayContainer()
rb.highlowcontainer.insertNewKeyValueAt(-i-1, hb, newac.iaddReturnMinimized(lowbits(x)))
return true
}
// AddInt adds the integer x to the bitmap (convenience method: the parameter is casted to uint32 and we call Add)
func (rb *Bitmap) AddInt(x int) {
rb.Add(uint32(x))
}
// Remove the integer x from the bitmap
func (rb *Bitmap) Remove(x uint32) {
hb := highbits(x)
i := rb.highlowcontainer.getIndex(hb)
if i >= 0 {
c := rb.highlowcontainer.getWritableContainerAtIndex(i).iremoveReturnMinimized(lowbits(x))
rb.highlowcontainer.setContainerAtIndex(i, c)
if rb.highlowcontainer.getContainerAtIndex(i).getCardinality() == 0 {
rb.highlowcontainer.removeAtIndex(i)
}
}
}
// CheckedRemove removes the integer x from the bitmap and return true if the integer was effectively remove (and false if the integer was not present)
func (rb *Bitmap) CheckedRemove(x uint32) bool {
// TODO: add unit tests for this method
hb := highbits(x)
i := rb.highlowcontainer.getIndex(hb)
if i >= 0 {
C := rb.highlowcontainer.getWritableContainerAtIndex(i)
oldcard := C.getCardinality()
C = C.iremoveReturnMinimized(lowbits(x))
rb.highlowcontainer.setContainerAtIndex(i, C)
if rb.highlowcontainer.getContainerAtIndex(i).getCardinality() == 0 {
rb.highlowcontainer.removeAtIndex(i)
return true
}
return C.getCardinality() < oldcard
}
return false
}
// IsEmpty returns true if the Bitmap is empty (it is faster than doing (GetCardinality() == 0))
func (rb *Bitmap) IsEmpty() bool {
return rb.highlowcontainer.size() == 0
}
// GetCardinality returns the number of integers contained in the bitmap
func (rb *Bitmap) GetCardinality() uint64 {
size := uint64(0)
for _, c := range rb.highlowcontainer.containers {
size += uint64(c.getCardinality())
}
return size
}
// Rank returns the number of integers that are smaller or equal to x (Rank(infinity) would be GetCardinality())
func (rb *Bitmap) Rank(x uint32) uint64 {
size := uint64(0)
for i := 0; i < rb.highlowcontainer.size(); i++ {
key := rb.highlowcontainer.getKeyAtIndex(i)
if key > highbits(x) {
return size
}
if key < highbits(x) {
size += uint64(rb.highlowcontainer.getContainerAtIndex(i).getCardinality())
} else {
return size + uint64(rb.highlowcontainer.getContainerAtIndex(i).rank(lowbits(x)))
}
}
return size
}
// Select returns the xth integer in the bitmap
func (rb *Bitmap) Select(x uint32) (uint32, error) {
if rb.GetCardinality() <= uint64(x) {
return 0, fmt.Errorf("can't find %dth integer in a bitmap with only %d items", x, rb.GetCardinality())
}
remaining := x
for i := 0; i < rb.highlowcontainer.size(); i++ {
c := rb.highlowcontainer.getContainerAtIndex(i)
if remaining >= uint32(c.getCardinality()) {
remaining -= uint32(c.getCardinality())
} else {
key := rb.highlowcontainer.getKeyAtIndex(i)
return uint32(key)<<16 + uint32(c.selectInt(uint16(remaining))), nil
}
}
return 0, fmt.Errorf("can't find %dth integer in a bitmap with only %d items", x, rb.GetCardinality())
}
// And computes the intersection between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) And(x2 *Bitmap) {
pos1 := 0
pos2 := 0
intersectionsize := 0
length1 := rb.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
main:
for {
if pos1 < length1 && pos2 < length2 {
s1 := rb.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 == s2 {
c1 := rb.highlowcontainer.getWritableContainerAtIndex(pos1)
c2 := x2.highlowcontainer.getContainerAtIndex(pos2)
diff := c1.iand(c2)
if diff.getCardinality() > 0 {
rb.highlowcontainer.replaceKeyAndContainerAtIndex(intersectionsize, s1, diff, false)
intersectionsize++
}
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else if s1 < s2 {
pos1 = rb.highlowcontainer.advanceUntil(s2, pos1)
if pos1 == length1 {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
} else {
break
}
}
rb.highlowcontainer.resize(intersectionsize)
}
// OrCardinality returns the cardinality of the union between two bitmaps, bitmaps are not modified
func (rb *Bitmap) OrCardinality(x2 *Bitmap) uint64 {
pos1 := 0
pos2 := 0
length1 := rb.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
answer := uint64(0)
main:
for {
if (pos1 < length1) && (pos2 < length2) {
s1 := rb.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 < s2 {
answer += uint64(rb.highlowcontainer.getContainerAtIndex(pos1).getCardinality())
pos1++
if pos1 == length1 {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else if s1 > s2 {
answer += uint64(x2.highlowcontainer.getContainerAtIndex(pos2).getCardinality())
pos2++
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else {
// TODO: could be faster if we did not have to materialize the container
answer += uint64(rb.highlowcontainer.getContainerAtIndex(pos1).or(x2.highlowcontainer.getContainerAtIndex(pos2)).getCardinality())
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
} else {
break
}
}
for ; pos1 < length1; pos1++ {
answer += uint64(rb.highlowcontainer.getContainerAtIndex(pos1).getCardinality())
}
for ; pos2 < length2; pos2++ {
answer += uint64(x2.highlowcontainer.getContainerAtIndex(pos2).getCardinality())
}
return answer
}
// AndCardinality returns the cardinality of the intersection between two bitmaps, bitmaps are not modified
func (rb *Bitmap) AndCardinality(x2 *Bitmap) uint64 {
pos1 := 0
pos2 := 0
answer := uint64(0)
length1 := rb.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
main:
for {
if pos1 < length1 && pos2 < length2 {
s1 := rb.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 == s2 {
c1 := rb.highlowcontainer.getContainerAtIndex(pos1)
c2 := x2.highlowcontainer.getContainerAtIndex(pos2)
answer += uint64(c1.andCardinality(c2))
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else if s1 < s2 {
pos1 = rb.highlowcontainer.advanceUntil(s2, pos1)
if pos1 == length1 {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
} else {
break
}
}
return answer
}
// Intersects checks whether two bitmap intersects, bitmaps are not modified
func (rb *Bitmap) Intersects(x2 *Bitmap) bool {
pos1 := 0
pos2 := 0
length1 := rb.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
main:
for {
if pos1 < length1 && pos2 < length2 {
s1 := rb.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 == s2 {
c1 := rb.highlowcontainer.getContainerAtIndex(pos1)
c2 := x2.highlowcontainer.getContainerAtIndex(pos2)
if c1.intersects(c2) {
return true
}
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else if s1 < s2 {
pos1 = rb.highlowcontainer.advanceUntil(s2, pos1)
if pos1 == length1 {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
} else {
break
}
}
return false
}
// Xor computes the symmetric difference between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) Xor(x2 *Bitmap) {
pos1 := 0
pos2 := 0
length1 := rb.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
for {
if (pos1 < length1) && (pos2 < length2) {
s1 := rb.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
if s1 < s2 {
pos1 = rb.highlowcontainer.advanceUntil(s2, pos1)
if pos1 == length1 {
break
}
} else if s1 > s2 {
c := x2.highlowcontainer.getWritableContainerAtIndex(pos2)
rb.highlowcontainer.insertNewKeyValueAt(pos1, x2.highlowcontainer.getKeyAtIndex(pos2), c)
length1++
pos1++
pos2++
} else {
// TODO: couple be computed in-place for reduced memory usage
c := rb.highlowcontainer.getContainerAtIndex(pos1).xor(x2.highlowcontainer.getContainerAtIndex(pos2))
if c.getCardinality() > 0 {
rb.highlowcontainer.setContainerAtIndex(pos1, c)
pos1++
} else {
rb.highlowcontainer.removeAtIndex(pos1)
length1--
}
pos2++
}
} else {
break
}
}
if pos1 == length1 {
rb.highlowcontainer.appendCopyMany(x2.highlowcontainer, pos2, length2)
}
}
// Or computes the union between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) Or(x2 *Bitmap) {
results := Or(rb, x2) // Todo: could be computed in-place for reduced memory usage
rb.highlowcontainer = results.highlowcontainer
}
// AndNot computes the difference between two bitmaps and stores the result in the current bitmap
func (rb *Bitmap) AndNot(x2 *Bitmap) {
pos1 := 0
pos2 := 0
intersectionsize := 0
length1 := rb.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
main:
for {
if pos1 < length1 && pos2 < length2 {
s1 := rb.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 == s2 {
c1 := rb.highlowcontainer.getWritableContainerAtIndex(pos1)
c2 := x2.highlowcontainer.getContainerAtIndex(pos2)
diff := c1.iandNot(c2)
if diff.getCardinality() > 0 {
rb.highlowcontainer.replaceKeyAndContainerAtIndex(intersectionsize, s1, diff, false)
intersectionsize++
}
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else if s1 < s2 {
c1 := rb.highlowcontainer.getContainerAtIndex(pos1)
mustCopyOnWrite := rb.highlowcontainer.needsCopyOnWrite(pos1)
rb.highlowcontainer.replaceKeyAndContainerAtIndex(intersectionsize, s1, c1, mustCopyOnWrite)
intersectionsize++
pos1++
if pos1 == length1 {
break main
}
s1 = rb.highlowcontainer.getKeyAtIndex(pos1)
} else { //s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
} else {
break
}
}
// TODO:implement as a copy
for pos1 < length1 {
c1 := rb.highlowcontainer.getContainerAtIndex(pos1)
s1 := rb.highlowcontainer.getKeyAtIndex(pos1)
mustCopyOnWrite := rb.highlowcontainer.needsCopyOnWrite(pos1)
rb.highlowcontainer.replaceKeyAndContainerAtIndex(intersectionsize, s1, c1, mustCopyOnWrite)
intersectionsize++
pos1++
}
rb.highlowcontainer.resize(intersectionsize)
}
// Or computes the union between two bitmaps and returns the result
func Or(x1, x2 *Bitmap) *Bitmap {
answer := NewBitmap()
pos1 := 0
pos2 := 0
length1 := x1.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
main:
for (pos1 < length1) && (pos2 < length2) {
s1 := x1.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 < s2 {
answer.highlowcontainer.appendCopy(x1.highlowcontainer, pos1)
pos1++
if pos1 == length1 {
break main
}
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
} else if s1 > s2 {
answer.highlowcontainer.appendCopy(x2.highlowcontainer, pos2)
pos2++
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else {
answer.highlowcontainer.appendContainer(s1, x1.highlowcontainer.getContainerAtIndex(pos1).or(x2.highlowcontainer.getContainerAtIndex(pos2)), false)
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
}
if pos1 == length1 {
answer.highlowcontainer.appendCopyMany(x2.highlowcontainer, pos2, length2)
} else if pos2 == length2 {
answer.highlowcontainer.appendCopyMany(x1.highlowcontainer, pos1, length1)
}
return answer
}
// And computes the intersection between two bitmaps and returns the result
func And(x1, x2 *Bitmap) *Bitmap {
answer := NewBitmap()
pos1 := 0
pos2 := 0
length1 := x1.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
main:
for pos1 < length1 && pos2 < length2 {
s1 := x1.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 == s2 {
C := x1.highlowcontainer.getContainerAtIndex(pos1)
C = C.and(x2.highlowcontainer.getContainerAtIndex(pos2))
if C.getCardinality() > 0 {
answer.highlowcontainer.appendContainer(s1, C, false)
}
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else if s1 < s2 {
pos1 = x1.highlowcontainer.advanceUntil(s2, pos1)
if pos1 == length1 {
break main
}
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
} else { // s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
}
return answer
}
// Xor computes the symmetric difference between two bitmaps and returns the result
func Xor(x1, x2 *Bitmap) *Bitmap {
answer := NewBitmap()
pos1 := 0
pos2 := 0
length1 := x1.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
for {
if (pos1 < length1) && (pos2 < length2) {
s1 := x1.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
if s1 < s2 {
answer.highlowcontainer.appendCopy(x1.highlowcontainer, pos1)
pos1++
} else if s1 > s2 {
answer.highlowcontainer.appendCopy(x2.highlowcontainer, pos2)
pos2++
} else {
c := x1.highlowcontainer.getContainerAtIndex(pos1).xor(x2.highlowcontainer.getContainerAtIndex(pos2))
if c.getCardinality() > 0 {
answer.highlowcontainer.appendContainer(s1, c, false)
}
pos1++
pos2++
}
} else {
break
}
}
if pos1 == length1 {
answer.highlowcontainer.appendCopyMany(x2.highlowcontainer, pos2, length2)
} else if pos2 == length2 {
answer.highlowcontainer.appendCopyMany(x1.highlowcontainer, pos1, length1)
}
return answer
}
// AndNot computes the difference between two bitmaps and returns the result
func AndNot(x1, x2 *Bitmap) *Bitmap {
answer := NewBitmap()
pos1 := 0
pos2 := 0
length1 := x1.highlowcontainer.size()
length2 := x2.highlowcontainer.size()
main:
for {
if pos1 < length1 && pos2 < length2 {
s1 := x1.highlowcontainer.getKeyAtIndex(pos1)
s2 := x2.highlowcontainer.getKeyAtIndex(pos2)
for {
if s1 < s2 {
answer.highlowcontainer.appendCopy(x1.highlowcontainer, pos1)
pos1++
if pos1 == length1 {
break main
}
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
} else if s1 == s2 {
c1 := x1.highlowcontainer.getContainerAtIndex(pos1)
c2 := x2.highlowcontainer.getContainerAtIndex(pos2)
diff := c1.andNot(c2)
if diff.getCardinality() > 0 {
answer.highlowcontainer.appendContainer(s1, diff, false)
}
pos1++
pos2++
if (pos1 == length1) || (pos2 == length2) {
break main
}
s1 = x1.highlowcontainer.getKeyAtIndex(pos1)
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
} else { //s1 > s2
pos2 = x2.highlowcontainer.advanceUntil(s1, pos2)
if pos2 == length2 {
break main
}
s2 = x2.highlowcontainer.getKeyAtIndex(pos2)
}
}
} else {
break
}
}
if pos2 == length2 {
answer.highlowcontainer.appendCopyMany(x1.highlowcontainer, pos1, length1)
}
return answer
}
// AddMany add all of the values in dat
func (rb *Bitmap) AddMany(dat []uint32) {
if len(dat) == 0 {
return
}
prev := dat[0]
idx, c := rb.addwithptr(prev)
for _, i := range dat[1:] {
if highbits(prev) == highbits(i) {
c = c.iaddReturnMinimized(lowbits(i))
rb.highlowcontainer.setContainerAtIndex(idx, c)
} else {
idx, c = rb.addwithptr(i)
}
prev = i
}
}
// BitmapOf generates a new bitmap filled with the specified integers
func BitmapOf(dat ...uint32) *Bitmap {
ans := NewBitmap()
ans.AddMany(dat)
return ans
}
// Flip negates the bits in the given range (i.e., [rangeStart,rangeEnd)), any integer present in this range and in the bitmap is removed,
// and any integer present in the range and not in the bitmap is added.
// The function uses 64-bit parameters even though a Bitmap stores 32-bit values because it is allowed and meaningful to use [0,uint64(0x100000000)) as a range
// while uint64(0x100000000) cannot be represented as a 32-bit value.
func (rb *Bitmap) Flip(rangeStart, rangeEnd uint64) {
if rangeEnd > MaxUint32+1 {
panic("rangeEnd > MaxUint32+1")
}
if rangeStart > MaxUint32+1 {
panic("rangeStart > MaxUint32+1")
}
if rangeStart >= rangeEnd {
return
}
hbStart := highbits(uint32(rangeStart))
lbStart := lowbits(uint32(rangeStart))
hbLast := highbits(uint32(rangeEnd - 1))
lbLast := lowbits(uint32(rangeEnd - 1))
var max uint32 = maxLowBit
for hb := hbStart; hb <= hbLast; hb++ {
var containerStart uint32
if hb == hbStart {
containerStart = uint32(lbStart)
}
containerLast := max
if hb == hbLast {
containerLast = uint32(lbLast)
}
i := rb.highlowcontainer.getIndex(hb)
if i >= 0 {
c := rb.highlowcontainer.getWritableContainerAtIndex(i).inot(int(containerStart), int(containerLast)+1)
if c.getCardinality() > 0 {
rb.highlowcontainer.setContainerAtIndex(i, c)
} else {
rb.highlowcontainer.removeAtIndex(i)
}
} else { // *think* the range of ones must never be
// empty.
rb.highlowcontainer.insertNewKeyValueAt(-i-1, hb, rangeOfOnes(int(containerStart), int(containerLast)))
}
}
}
// FlipInt calls Flip after casting the parameters (convenience method)
func (rb *Bitmap) FlipInt(rangeStart, rangeEnd int) {
rb.Flip(uint64(rangeStart), uint64(rangeEnd))
}
// AddRange adds the integers in [rangeStart, rangeEnd) to the bitmap.
// The function uses 64-bit parameters even though a Bitmap stores 32-bit values because it is allowed and meaningful to use [0,uint64(0x100000000)) as a range
// while uint64(0x100000000) cannot be represented as a 32-bit value.
func (rb *Bitmap) AddRange(rangeStart, rangeEnd uint64) {
if rangeStart >= rangeEnd {
return
}
hbStart := uint32(highbits(uint32(rangeStart)))
lbStart := uint32(lowbits(uint32(rangeStart)))
hbLast := uint32(highbits(uint32(rangeEnd - 1)))
lbLast := uint32(lowbits(uint32(rangeEnd - 1)))
var max uint32 = maxLowBit
for hb := uint16(hbStart); hb <= uint16(hbLast); hb++ {
containerStart := uint32(0)
if hb == uint16(hbStart) {
containerStart = lbStart
}
containerLast := max
if hb == uint16(hbLast) {
containerLast = lbLast
}
i := rb.highlowcontainer.getIndex(hb)
if i >= 0 {
c := rb.highlowcontainer.getWritableContainerAtIndex(i).iaddRange(int(containerStart), int(containerLast)+1)
rb.highlowcontainer.setContainerAtIndex(i, c)
} else { // *think* the range of ones must never be
// empty.
rb.highlowcontainer.insertNewKeyValueAt(-i-1, hb, rangeOfOnes(int(containerStart), int(containerLast)))
}
}
}
// RemoveRange removes the integers in [rangeStart, rangeEnd) from the bitmap.
// The function uses 64-bit parameters even though a Bitmap stores 32-bit values because it is allowed and meaningful to use [0,uint64(0x100000000)) as a range
// while uint64(0x100000000) cannot be represented as a 32-bit value.
func (rb *Bitmap) RemoveRange(rangeStart, rangeEnd uint64) {
if rangeStart >= rangeEnd {
return
}
hbStart := uint32(highbits(uint32(rangeStart)))
lbStart := uint32(lowbits(uint32(rangeStart)))
hbLast := uint32(highbits(uint32(rangeEnd - 1)))
lbLast := uint32(lowbits(uint32(rangeEnd - 1)))
var max uint32 = maxLowBit
if hbStart == hbLast {
i := rb.highlowcontainer.getIndex(uint16(hbStart))
if i < 0 {
return
}
c := rb.highlowcontainer.getWritableContainerAtIndex(i).iremoveRange(int(lbStart), int(lbLast+1))
if c.getCardinality() > 0 {
rb.highlowcontainer.setContainerAtIndex(i, c)
} else {
rb.highlowcontainer.removeAtIndex(i)
}
return
}
ifirst := rb.highlowcontainer.getIndex(uint16(hbStart))
ilast := rb.highlowcontainer.getIndex(uint16(hbLast))
if ifirst >= 0 {
if lbStart != 0 {
c := rb.highlowcontainer.getWritableContainerAtIndex(ifirst).iremoveRange(int(lbStart), int(max+1))
if c.getCardinality() > 0 {
rb.highlowcontainer.setContainerAtIndex(ifirst, c)
ifirst++
}
}
} else {
ifirst = -ifirst - 1
}
if ilast >= 0 {
if lbLast != max {
c := rb.highlowcontainer.getWritableContainerAtIndex(ilast).iremoveRange(int(0), int(lbLast+1))
if c.getCardinality() > 0 {
rb.highlowcontainer.setContainerAtIndex(ilast, c)
} else {
ilast++
}
} else {
ilast++
}
} else {
ilast = -ilast - 1
}
rb.highlowcontainer.removeIndexRange(ifirst, ilast)
}
// Flip negates the bits in the given range (i.e., [rangeStart,rangeEnd)), any integer present in this range and in the bitmap is removed,
// and any integer present in the range and not in the bitmap is added, a new bitmap is returned leaving
// the current bitmap unchanged.
// The function uses 64-bit parameters even though a Bitmap stores 32-bit values because it is allowed and meaningful to use [0,uint64(0x100000000)) as a range
// while uint64(0x100000000) cannot be represented as a 32-bit value.
func Flip(bm *Bitmap, rangeStart, rangeEnd uint64) *Bitmap {
if rangeStart >= rangeEnd {
return bm.Clone()
}
if rangeStart > MaxUint32 {
panic("rangeStart > MaxUint32")
}
if rangeEnd-1 > MaxUint32 {
panic("rangeEnd-1 > MaxUint32")
}
answer := NewBitmap()
hbStart := highbits(uint32(rangeStart))
lbStart := lowbits(uint32(rangeStart))
hbLast := highbits(uint32(rangeEnd - 1))
lbLast := lowbits(uint32(rangeEnd - 1))
// copy the containers before the active area
answer.highlowcontainer.appendCopiesUntil(bm.highlowcontainer, hbStart)
var max uint32 = maxLowBit
for hb := hbStart; hb <= hbLast; hb++ {
var containerStart uint32
if hb == hbStart {
containerStart = uint32(lbStart)
}
containerLast := max
if hb == hbLast {
containerLast = uint32(lbLast)
}
i := bm.highlowcontainer.getIndex(hb)
j := answer.highlowcontainer.getIndex(hb)
if i >= 0 {
c := bm.highlowcontainer.getContainerAtIndex(i).not(int(containerStart), int(containerLast)+1)
if c.getCardinality() > 0 {
answer.highlowcontainer.insertNewKeyValueAt(-j-1, hb, c)
}
} else { // *think* the range of ones must never be
// empty.
answer.highlowcontainer.insertNewKeyValueAt(-j-1, hb,
rangeOfOnes(int(containerStart), int(containerLast)))
}
}
// copy the containers after the active area.
answer.highlowcontainer.appendCopiesAfter(bm.highlowcontainer, hbLast)
return answer
}
// SetCopyOnWrite sets this bitmap to use copy-on-write so that copies are fast and memory conscious
// if the parameter is true, otherwise we leave the default where hard copies are made
// (copy-on-write requires extra care in a threaded context).
func (rb *Bitmap) SetCopyOnWrite(val bool) {
rb.highlowcontainer.copyOnWrite = val
}
// GetCopyOnWrite gets this bitmap's copy-on-write property
func (rb *Bitmap) GetCopyOnWrite() (val bool) {
return rb.highlowcontainer.copyOnWrite
}
// FlipInt calls Flip after casting the parameters (convenience method)
func FlipInt(bm *Bitmap, rangeStart, rangeEnd int) *Bitmap {
return Flip(bm, uint64(rangeStart), uint64(rangeEnd))
}
// Statistics provides details on the container types in use.
type Statistics struct {
Cardinality uint64
Containers uint64
ArrayContainers uint64
ArrayContainerBytes uint64
ArrayContainerValues uint64
BitmapContainers uint64
BitmapContainerBytes uint64
BitmapContainerValues uint64
RunContainers uint64
RunContainerBytes uint64
RunContainerValues uint64
}
// Stats returns details on container type usage in a Statistics struct.
func (rb *Bitmap) Stats() Statistics {
stats := Statistics{}
stats.Containers = uint64(len(rb.highlowcontainer.containers))
for _, c := range rb.highlowcontainer.containers {
stats.Cardinality += uint64(c.getCardinality())
switch c.(type) {
case *arrayContainer:
stats.ArrayContainers++
stats.ArrayContainerBytes += uint64(c.getSizeInBytes())
stats.ArrayContainerValues += uint64(c.getCardinality())
case *bitmapContainer:
stats.BitmapContainers++
stats.BitmapContainerBytes += uint64(c.getSizeInBytes())
stats.BitmapContainerValues += uint64(c.getCardinality())
case *runContainer16:
stats.RunContainers++
stats.RunContainerBytes += uint64(c.getSizeInBytes())
stats.RunContainerValues += uint64(c.getCardinality())
}
}
return stats
}