diff --git a/cmd/pilosa/main.go b/cmd/pilosa/main.go index cef5b4b6e..c15585747 100644 --- a/cmd/pilosa/main.go +++ b/cmd/pilosa/main.go @@ -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 } diff --git a/executor.go b/executor.go index 88cb2311c..d208c813a 100644 --- a/executor.go +++ b/executor.go @@ -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)) } diff --git a/executor_test.go b/executor_test.go index 7a4d02613..b55c408e7 100644 --- a/executor_test.go +++ b/executor_test.go @@ -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. diff --git a/fragment.go b/fragment.go index c0f1bef0d..c3aeabacc 100644 --- a/fragment.go +++ b/fragment.go @@ -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 } } diff --git a/handler.go b/handler.go index 9c1896202..188473640 100644 --- a/handler.go +++ b/handler.go @@ -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)) } diff --git a/index.go b/index.go index 8192d0010..56a2a5ef9 100644 --- a/index.go +++ b/index.go @@ -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 +} diff --git a/internal/internal.pb.go b/internal/internal.pb.go index 9ca780d28..b28f58f3d 100644 --- a/internal/internal.pb.go +++ b/internal/internal.pb.go @@ -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") } diff --git a/internal/internal.proto b/internal/internal.proto index 81dd0d564..aaa34a6a2 100644 --- a/internal/internal.proto +++ b/internal/internal.proto @@ -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; +} diff --git a/roaring/roaring.go b/roaring/roaring.go index 6aa49579c..a87d4f5c1 100644 --- a/roaring/roaring.go +++ b/roaring/roaring.go @@ -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.