Merge pull request #54 from umbel/todd

fixed issue with SetBit/ClearBit not correctly reporting bitchange
This commit is contained in:
tgruben 2016-02-26 12:13:41 -06:00
commit a12ec57eec
9 changed files with 279 additions and 89 deletions

View file

@ -5,9 +5,11 @@ import (
"flag"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"os/signal"
"os/user"
@ -18,7 +20,9 @@ import (
"time"
"github.com/BurntSushi/toml"
"github.com/gogo/protobuf/proto"
"github.com/umbel/pilosa"
"github.com/umbel/pilosa/internal"
)
// Build holds the build information passed in at compile time.
@ -72,8 +76,10 @@ func main() {
// Main represents the main program execution.
type Main struct {
index *pilosa.Index
ln net.Listener
index *pilosa.Index
ln net.Listener
ticker *time.Ticker
pollingSecs int
// Path to the configuration file.
ConfigPath string
@ -94,7 +100,6 @@ type Main struct {
func NewMain() *Main {
return &Main{
Config: NewConfig(),
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
@ -176,13 +181,84 @@ func (m *Main) Run(args ...string) error {
// Serve HTTP.
go func() { http.Serve(ln, h) }()
//sync up max slice if more than one node
if len(cluster.Nodes) > 1 {
m.ticker = time.NewTicker(time.Second * time.Duration(m.pollingSecs))
go func() {
for range m.ticker.C {
oldmax:= m.index.SliceN()
newmax:=oldmax
for _, node := range cluster.Nodes {
if hostname != node.Host {
newslice,_:=checkMaxSlice(node.Host)
if newslice>newmax{
newmax= newslice
}
}
}
if newmax>oldmax{
m.index.SetMax(newmax)
}
}
}()
}
fmt.Fprintf(m.Stderr, "Listening as http://%s\n", hostname)
return nil
}
func checkMaxSlice(hostport string) (uint64, error) {
// Create HTTP request.
req, err := http.NewRequest("GET", (&url.URL{
Scheme: "http",
Host: hostport,
Path: "/slices/max",
}).String(), nil)
if err != nil {
return 0, err
}
// Require protobuf encoding.
req.Header.Set("Accept", "application/x-protobuf")
req.Header.Set("Content-Type", "application/x-protobuf")
// Send request to remote node.
resp, err := http.DefaultClient.Do(req)
if err != nil {
return 0, err
}
defer resp.Body.Close()
// Read response into buffer.
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return 0, err
}
// Check status code.
if resp.StatusCode != http.StatusOK {
return 0, fmt.Errorf("invalid status: code=%d, err=%s", resp.StatusCode, body)
}
// Decode response object.
pb := internal.SliceMaxResponse{}
if err = proto.Unmarshal(body, &pb); err != nil {
return 0, err
}
return *pb.SliceMax, nil
}
// Close shuts down the process.
func (m *Main) Close() error {
if m.ticker != nil {
m.ticker.Stop()
}
if m.ln != nil {
m.ln.Close()
}
@ -200,6 +276,7 @@ func (m *Main) ParseFlags(args []string) error {
fs.SetOutput(m.Stderr)
fs.StringVar(&m.ConfigPath, "config", "", "config path")
fs.StringVar(&m.CPUProfile, "cpuprofile", "", "write cpu profile to file")
fs.IntVar(&m.pollingSecs, "pollingSecs", 60, "number of seconds to poll the cluster for maxslice")
if err := fs.Parse(args); err != nil {
return err
}

View file

@ -351,10 +351,11 @@ func (e *Executor) executeSetBit(db string, c *pql.SetBit, opt *ExecOptions) (bo
}
// Forward call to remote node otherwise.
if _, err := e.exec(node, db, &pql.Query{Root: c}, nil, opt); err != nil {
if res, err := e.exec(node, db, &pql.Query{Root: c}, nil, opt); err != nil {
return false, err
} else {
ret = res.(bool)
}
fmt.Println("NEED TO IMPLEMENT REMOTE SETBIT")
}
return ret, nil
}
@ -464,7 +465,7 @@ func (e *Executor) exec(node *Node, db string, q *pql.Query, slices []uint64, op
case *pql.Count:
return pb.GetN(), nil
case *pql.SetBit:
return nil, nil
return pb.GetChanged(), nil
default:
panic(fmt.Sprintf("invalid node for remote exec: %T", q.Root))
}

View file

@ -123,14 +123,29 @@ func TestExecutor_Execute_SetBit(t *testing.T) {
defer idx.Close()
e := NewExecutor(idx.Index, NewCluster(1))
if _, err := e.Execute("d", MustParse(`SetBit(id=10, frame=f, profileID=1)`), nil, nil); err != nil {
t.Fatal(err)
f := idx.MustCreateFragmentIfNotExists("d", "f", 0)
if n := f.Bitmap(11).Count(); n != 0 {
t.Fatalf("unexpected bitmap count: %d", n)
}
f := idx.MustCreateFragmentIfNotExists("d", "f", 0)
if n := f.Bitmap(10).Count(); n != 1 {
if res, err := e.Execute("d", MustParse(`SetBit(id=11, frame=f, profileID=1)`), nil); err != nil {
t.Fatal(err)
} else {
if !res.(bool) {
t.Fatalf("expected bit changed")
}
}
if n := f.Bitmap(11).Count(); n != 1 {
t.Fatalf("unexpected bitmap count: %d", n)
}
if res, err := e.Execute("d", MustParse(`SetBit(id=11, frame=f, profileID=1)`), nil); err != nil {
t.Fatal(err)
} else {
if res.(bool) {
t.Fatalf("expected bit unchanged")
}
}
}
// Ensure a SetBitmapAttrs() query can be executed.

View file

@ -329,18 +329,23 @@ func (f *Fragment) SetBit(bitmapID, profileID uint64, t *time.Time, q TimeQuantu
func (f *Fragment) setBit(bitmapID, profileID uint64) (changed bool, bool error) {
// Determine the position of the bit in the storage.
changed = false
pos, err := f.pos(bitmapID, profileID)
if err != nil {
return false, err
}
// Write to storage.
if err := f.storage.Add(pos); err != nil {
if changed, err = f.storage.Add(pos); err != nil {
return false, err
}
// Update the cache.
return f.bitmap(bitmapID).setBit(profileID), nil
if f.bitmap(bitmapID).setBit(profileID) {
changed = true
}
return changed, nil
}
@ -368,12 +373,16 @@ func (f *Fragment) ClearBit(bitmapID, profileID uint64) (bool, error) {
}
// Write to storage.
if err := f.storage.Remove(pos); err != nil {
changed, err := f.storage.Remove(pos)
if err != nil {
return false, err
}
// Update the cache.
return f.bitmap(bitmapID).clearBit(profileID), nil
if f.bitmap(bitmapID).clearBit(profileID) {
return true, nil
}
return changed, nil
}
@ -531,7 +540,7 @@ func (f *Fragment) Import(bitmapIDs, profileIDs []uint64) error {
}
// Write to storage.
if err := f.storage.Add(pos); err != nil {
if _, err := f.storage.Add(pos); err != nil {
return err
}
}

View file

@ -73,6 +73,13 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/slices/max":
switch r.Method {
case "GET":
h.handleGetSliceMax(w, r)
default:
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
case "/fragment/data":
switch r.Method {
case "GET":
@ -144,6 +151,24 @@ func (h *Handler) handlePostQuery(w http.ResponseWriter, r *http.Request) {
}
}
func (h *Handler) handleGetSliceMax(w http.ResponseWriter, r *http.Request) error {
sm := h.Index.SliceN()
if strings.Contains(r.Header.Get("Accept"), "application/x-protobuf") {
pb := &internal.SliceMaxResponse{
SliceMax: &sm,
}
if buf, err := proto.Marshal(pb); err != nil {
return err
} else if _, err := w.Write(buf); err != nil {
return err
}
return nil
}
resp := map[string]uint64{"SliceMax": sm}
return json.NewEncoder(w).Encode(resp)
}
// readProfiles returns a list of profile objects by id.
func (h *Handler) readProfiles(db *DB, ids []uint64) ([]*Profile, error) {
if db == nil {
@ -508,6 +533,8 @@ func encodeQueryResponse(resp *QueryResponse) *internal.QueryResponse {
pb.Pairs = encodePairs(result)
case uint64:
pb.N = proto.Uint64(result)
case bool:
pb.Changed = proto.Bool(result)
default:
panic(fmt.Sprintf("invalid query result type: %T", resp.Result))
}

View file

@ -9,8 +9,9 @@ import (
// Index represents a container for fragments.
type Index struct {
mu sync.Mutex
path string
mu sync.Mutex
path string
remoteMax uint64
// Databases by name.
dbs map[string]*DB
@ -19,8 +20,9 @@ type Index struct {
// NewIndex returns a new instance of Index.
func NewIndex(path string) *Index {
return &Index{
path: path,
dbs: make(map[string]*DB),
path: path,
dbs: make(map[string]*DB),
remoteMax: 0,
}
}
@ -72,7 +74,7 @@ func (i *Index) SliceN() uint64 {
i.mu.Lock()
defer i.mu.Unlock()
var sliceN uint64
sliceN := i.remoteMax
for _, db := range i.dbs {
if n := db.SliceN(); n > sliceN {
sliceN = n
@ -154,3 +156,8 @@ func (i *Index) CreateFragmentIfNotExists(db, frame string, slice uint64) (*Frag
}
return f.CreateFragmentIfNotExists(slice)
}
func (i *Index) SetMax(newmax uint64) {
i.mu.Lock()
defer i.mu.Unlock()
i.remoteMax = newmax
}

View file

@ -21,19 +21,22 @@ It has these top-level messages:
ImportRequest
ImportResponse
Cache
SliceMaxResponse
*/
package internal
import proto "github.com/gogo/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type Bitmap struct {
Chunks []*Chunk `protobuf:"bytes,1,rep" json:"Chunks,omitempty"`
Attrs []*Attr `protobuf:"bytes,2,rep" json:"Attrs,omitempty"`
Chunks []*Chunk `protobuf:"bytes,1,rep,name=Chunks" json:"Chunks,omitempty"`
Attrs []*Attr `protobuf:"bytes,2,rep,name=Attrs" json:"Attrs,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -56,8 +59,8 @@ func (m *Bitmap) GetAttrs() []*Attr {
}
type Chunk struct {
Key *uint64 `protobuf:"varint,1,req" json:"Key,omitempty"`
Value []uint64 `protobuf:"varint,2,rep" json:"Value,omitempty"`
Key *uint64 `protobuf:"varint,1,req,name=Key" json:"Key,omitempty"`
Value []uint64 `protobuf:"varint,2,rep,name=Value" json:"Value,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -80,8 +83,8 @@ func (m *Chunk) GetValue() []uint64 {
}
type Pair struct {
Key *uint64 `protobuf:"varint,1,req" json:"Key,omitempty"`
Count *uint64 `protobuf:"varint,2,req" json:"Count,omitempty"`
Key *uint64 `protobuf:"varint,1,req,name=Key" json:"Key,omitempty"`
Count *uint64 `protobuf:"varint,2,req,name=Count" json:"Count,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -104,8 +107,8 @@ func (m *Pair) GetCount() uint64 {
}
type Bit struct {
BitmapID *uint64 `protobuf:"varint,1,req" json:"BitmapID,omitempty"`
ProfileID *uint64 `protobuf:"varint,2,req" json:"ProfileID,omitempty"`
BitmapID *uint64 `protobuf:"varint,1,req,name=BitmapID" json:"BitmapID,omitempty"`
ProfileID *uint64 `protobuf:"varint,2,req,name=ProfileID" json:"ProfileID,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -128,8 +131,8 @@ func (m *Bit) GetProfileID() uint64 {
}
type Profile struct {
ID *uint64 `protobuf:"varint,1,req" json:"ID,omitempty"`
Attrs []*Attr `protobuf:"bytes,2,rep" json:"Attrs,omitempty"`
ID *uint64 `protobuf:"varint,1,req,name=ID" json:"ID,omitempty"`
Attrs []*Attr `protobuf:"bytes,2,rep,name=Attrs" json:"Attrs,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -152,10 +155,10 @@ func (m *Profile) GetAttrs() []*Attr {
}
type Attr struct {
Key *string `protobuf:"bytes,1,req" json:"Key,omitempty"`
StringValue *string `protobuf:"bytes,2,opt" json:"StringValue,omitempty"`
UintValue *uint64 `protobuf:"varint,3,opt" json:"UintValue,omitempty"`
BoolValue *bool `protobuf:"varint,4,opt" json:"BoolValue,omitempty"`
Key *string `protobuf:"bytes,1,req,name=Key" json:"Key,omitempty"`
StringValue *string `protobuf:"bytes,2,opt,name=StringValue" json:"StringValue,omitempty"`
UintValue *uint64 `protobuf:"varint,3,opt,name=UintValue" json:"UintValue,omitempty"`
BoolValue *bool `protobuf:"varint,4,opt,name=BoolValue" json:"BoolValue,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -192,7 +195,7 @@ func (m *Attr) GetBoolValue() bool {
}
type AttrMap struct {
Attrs []*Attr `protobuf:"bytes,1,rep" json:"Attrs,omitempty"`
Attrs []*Attr `protobuf:"bytes,1,rep,name=Attrs" json:"Attrs,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -208,12 +211,12 @@ func (m *AttrMap) GetAttrs() []*Attr {
}
type QueryRequest struct {
DB *string `protobuf:"bytes,1,req" json:"DB,omitempty"`
Query *string `protobuf:"bytes,2,req" json:"Query,omitempty"`
Slices []uint64 `protobuf:"varint,3,rep" json:"Slices,omitempty"`
Profiles *bool `protobuf:"varint,4,opt" json:"Profiles,omitempty"`
Timestamp *int64 `protobuf:"varint,5,opt" json:"Timestamp,omitempty"`
Quantum *uint32 `protobuf:"varint,6,opt" json:"Quantum,omitempty"`
DB *string `protobuf:"bytes,1,req,name=DB" json:"DB,omitempty"`
Query *string `protobuf:"bytes,2,req,name=Query" json:"Query,omitempty"`
Slices []uint64 `protobuf:"varint,3,rep,name=Slices" json:"Slices,omitempty"`
Profiles *bool `protobuf:"varint,4,opt,name=Profiles" json:"Profiles,omitempty"`
Timestamp *int64 `protobuf:"varint,5,opt,name=Timestamp" json:"Timestamp,omitempty"`
Quantum *uint32 `protobuf:"varint,6,opt,name=Quantum" json:"Quantum,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -264,11 +267,12 @@ func (m *QueryRequest) GetQuantum() uint32 {
}
type QueryResponse struct {
Err *string `protobuf:"bytes,1,opt" json:"Err,omitempty"`
Bitmap *Bitmap `protobuf:"bytes,2,opt" json:"Bitmap,omitempty"`
N *uint64 `protobuf:"varint,3,opt" json:"N,omitempty"`
Pairs []*Pair `protobuf:"bytes,4,rep" json:"Pairs,omitempty"`
Profiles []*Profile `protobuf:"bytes,5,rep" json:"Profiles,omitempty"`
Err *string `protobuf:"bytes,1,opt,name=Err" json:"Err,omitempty"`
Bitmap *Bitmap `protobuf:"bytes,2,opt,name=Bitmap" json:"Bitmap,omitempty"`
N *uint64 `protobuf:"varint,3,opt,name=N" json:"N,omitempty"`
Pairs []*Pair `protobuf:"bytes,4,rep,name=Pairs" json:"Pairs,omitempty"`
Profiles []*Profile `protobuf:"bytes,5,rep,name=Profiles" json:"Profiles,omitempty"`
Changed *bool `protobuf:"varint,6,opt,name=Changed" json:"Changed,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -311,12 +315,19 @@ func (m *QueryResponse) GetProfiles() []*Profile {
return nil
}
func (m *QueryResponse) GetChanged() bool {
if m != nil && m.Changed != nil {
return *m.Changed
}
return false
}
type ImportRequest struct {
DB *string `protobuf:"bytes,1,req" json:"DB,omitempty"`
Frame *string `protobuf:"bytes,2,req" json:"Frame,omitempty"`
Slice *uint64 `protobuf:"varint,3,req" json:"Slice,omitempty"`
BitmapIDs []uint64 `protobuf:"varint,4,rep" json:"BitmapIDs,omitempty"`
ProfileIDs []uint64 `protobuf:"varint,5,rep" json:"ProfileIDs,omitempty"`
DB *string `protobuf:"bytes,1,req,name=DB" json:"DB,omitempty"`
Frame *string `protobuf:"bytes,2,req,name=Frame" json:"Frame,omitempty"`
Slice *uint64 `protobuf:"varint,3,req,name=Slice" json:"Slice,omitempty"`
BitmapIDs []uint64 `protobuf:"varint,4,rep,name=BitmapIDs" json:"BitmapIDs,omitempty"`
ProfileIDs []uint64 `protobuf:"varint,5,rep,name=ProfileIDs" json:"ProfileIDs,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -360,7 +371,7 @@ func (m *ImportRequest) GetProfileIDs() []uint64 {
}
type ImportResponse struct {
Err *string `protobuf:"bytes,1,opt" json:"Err,omitempty"`
Err *string `protobuf:"bytes,1,opt,name=Err" json:"Err,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -376,7 +387,7 @@ func (m *ImportResponse) GetErr() string {
}
type Cache struct {
BitmapIDs []uint64 `protobuf:"varint,1,rep" json:"BitmapIDs,omitempty"`
BitmapIDs []uint64 `protobuf:"varint,1,rep,name=BitmapIDs" json:"BitmapIDs,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
@ -391,5 +402,34 @@ func (m *Cache) GetBitmapIDs() []uint64 {
return nil
}
func init() {
type SliceMaxResponse struct {
SliceMax *uint64 `protobuf:"varint,1,req,name=SliceMax" json:"SliceMax,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *SliceMaxResponse) Reset() { *m = SliceMaxResponse{} }
func (m *SliceMaxResponse) String() string { return proto.CompactTextString(m) }
func (*SliceMaxResponse) ProtoMessage() {}
func (m *SliceMaxResponse) GetSliceMax() uint64 {
if m != nil && m.SliceMax != nil {
return *m.SliceMax
}
return 0
}
func init() {
proto.RegisterType((*Bitmap)(nil), "internal.Bitmap")
proto.RegisterType((*Chunk)(nil), "internal.Chunk")
proto.RegisterType((*Pair)(nil), "internal.Pair")
proto.RegisterType((*Bit)(nil), "internal.Bit")
proto.RegisterType((*Profile)(nil), "internal.Profile")
proto.RegisterType((*Attr)(nil), "internal.Attr")
proto.RegisterType((*AttrMap)(nil), "internal.AttrMap")
proto.RegisterType((*QueryRequest)(nil), "internal.QueryRequest")
proto.RegisterType((*QueryResponse)(nil), "internal.QueryResponse")
proto.RegisterType((*ImportRequest)(nil), "internal.ImportRequest")
proto.RegisterType((*ImportResponse)(nil), "internal.ImportResponse")
proto.RegisterType((*Cache)(nil), "internal.Cache")
proto.RegisterType((*SliceMaxResponse)(nil), "internal.SliceMaxResponse")
}

View file

@ -51,6 +51,7 @@ message QueryResponse {
optional uint64 N = 3;
repeated Pair Pairs = 4;
repeated Profile Profiles = 5;
optional bool Changed = 6;
}
message ImportRequest {
@ -68,3 +69,7 @@ message ImportResponse {
message Cache {
repeated uint64 BitmapIDs = 1;
}
message SliceMaxResponse {
required uint64 SliceMax = 1;
}

View file

@ -41,24 +41,28 @@ func NewBitmap(a ...uint64) *Bitmap {
}
// Add adds values to the bitmap.
func (b *Bitmap) Add(a ...uint64) error {
func (b *Bitmap) Add(a ...uint64) (changed bool, err error) {
changed = false
for _, v := range a {
// Create an add operation.
op := &op{typ: opTypeAdd, value: v}
// Write operation to op log.
if err := b.writeOp(op); err != nil {
return err
return false, err
}
// Apply to the in-memory bitmap.
op.apply(b)
if op.apply(b) {
changed = true
}
}
return nil
return changed, nil
}
func (b *Bitmap) add(v uint64) {
func (b *Bitmap) add(v uint64) bool {
hb := highbits(v)
i := search64(b.keys, hb)
@ -69,7 +73,7 @@ func (b *Bitmap) add(v uint64) {
i = -i - 1
}
b.containers[i].add(lowbits(v))
return b.containers[i].add(lowbits(v))
}
// Contains returns true if v is in the bitmap.
@ -82,29 +86,32 @@ func (b *Bitmap) Contains(v uint64) bool {
}
// Remove removes values from the bitmap.
func (b *Bitmap) Remove(a ...uint64) error {
func (b *Bitmap) Remove(a ...uint64) (changed bool, err error) {
changed = false
for _, v := range a {
// Create an add operation.
op := &op{typ: opTypeRemove, value: v}
// Write operation to op log.
if err := b.writeOp(op); err != nil {
return err
return false, err
}
// Apply operation to the bitmap.
op.apply(b)
if op.apply(b) {
changed = true
}
}
return nil
return changed, nil
}
func (b *Bitmap) remove(v uint64) {
func (b *Bitmap) remove(v uint64) bool {
hb := highbits(v)
i := search64(b.keys, hb)
if i < 0 {
return
return false
}
b.containers[i].remove(lowbits(v))
return b.containers[i].remove(lowbits(v))
}
// Slice returns a slice of all integers in the bitmap.
@ -429,34 +436,32 @@ func (c *container) unmap() {
}
// add adds a value to the container.
func (c *container) add(v uint16) {
func (c *container) add(v uint16) bool {
if c.isArray() {
c.arrayAdd(v)
return
return c.arrayAdd(v)
}
c.bitmapAdd(v)
return c.bitmapAdd(v)
}
func (c *container) arrayAdd(v uint16) {
func (c *container) arrayAdd(v uint16) bool {
// Optimize appending to the end of an array container.
if c.n > 0 && c.n < arrayMaxSize && c.isArray() && c.array[c.n-1] < v {
c.unmap()
c.array = append(c.array, v)
c.n++
return
return true
}
// Find index of the integer in the container. Exit if it already exists.
i := search16(c.array, v)
if i >= 0 {
return
return false
}
// Convert to a bitmap container if too many values are in an array container.
if c.n >= arrayMaxSize {
c.convertToBitmap()
c.bitmapAdd(v)
return
return c.bitmapAdd(v)
}
// Otherwise insert into array.
@ -466,15 +471,17 @@ func (c *container) arrayAdd(v uint16) {
copy(c.array[i+1:], c.array[i:])
c.array[i] = v
c.n++
return true
}
func (c *container) bitmapAdd(v uint16) {
func (c *container) bitmapAdd(v uint16) bool {
if c.bitmapContains(v) {
return
return false
}
c.unmap()
c.bitmap[v/64] |= (1 << uint64(v%64))
c.n++
return true
}
// contains returns true if v is in the container.
@ -494,28 +501,28 @@ func (c *container) bitmapContains(v uint16) bool {
}
// remove adds a value to the container.
func (c *container) remove(v uint16) {
func (c *container) remove(v uint16) bool {
if c.isArray() {
c.arrayRemove(v)
return
return c.arrayRemove(v)
}
c.bitmapRemove(v)
return c.bitmapRemove(v)
}
func (c *container) arrayRemove(v uint16) {
func (c *container) arrayRemove(v uint16) bool {
i := search16(c.array, v)
if i < 0 {
return
return false
}
c.unmap()
c.n--
c.array = append(c.array[:i], c.array[i+1:]...)
return true
}
func (c *container) bitmapRemove(v uint16) {
func (c *container) bitmapRemove(v uint16) bool {
if !c.bitmapContains(v) {
return
return false
}
c.unmap()
@ -527,6 +534,7 @@ func (c *container) bitmapRemove(v uint16) {
if c.n == arrayMaxSize {
c.convertToArray()
}
return true
}
// convertToArray converts the values in the bitmap to array values.
@ -594,15 +602,16 @@ type op struct {
}
// apply executes the operation against a bitmap.
func (op *op) apply(b *Bitmap) {
func (op *op) apply(b *Bitmap) bool {
switch op.typ {
case opTypeAdd:
b.add(op.value)
return b.add(op.value)
case opTypeRemove:
b.remove(op.value)
return b.remove(op.value)
default:
panic(fmt.Sprintf("invalid op type: %d", op.typ))
}
return false
}
// WriteTo writes op to the w.