1
1
package bitset
2
2
3
+ import (
4
+ "bufio"
5
+ "encoding/binary"
6
+ "fmt"
7
+ "os"
8
+ "time"
9
+ )
10
+
3
11
const word = uint64 (64 )
4
12
const logword = uint (6 )
13
+ const bufsize = 1 << 16
14
+
15
+ // each channel write is 9 bytes, so if we were to write full disk sector
16
+ // of 64K bytes at once, we'd flush ~7112 writes
17
+ const channelsize = 7200
18
+
19
+ const (
20
+ new = iota
21
+ set
22
+ clear
23
+ flip
24
+ )
25
+
26
+ type command struct {
27
+ code uint8
28
+ index uint64
29
+ }
5
30
6
31
type BitSet struct {
7
- length uint64
8
- bits []uint64
32
+ length uint64
33
+ bits []uint64
34
+ writes chan command
35
+ binlogfile string
9
36
}
10
37
11
38
func getSize (length uint64 ) uint64 {
12
39
return uint64 ((length + word - 1 ) / word )
13
40
}
14
41
15
- func New (length uint64 ) * BitSet {
42
+ func flush (filename string , writes chan command ) {
43
+ f , err := os .Create (filename )
44
+ if err != nil {
45
+ panic (err )
46
+ }
47
+
48
+ defer func () {
49
+ if err := f .Close (); err != nil {
50
+ panic (err )
51
+ }
52
+ }()
53
+
54
+ // make a buffered writer
55
+ w := bufio .NewWriterSize (f , bufsize )
56
+
57
+ ticker := time .NewTimer (time .Second )
58
+
59
+ buf := make ([]byte , bufsize )
60
+ long := make ([]byte , 8 )
61
+
62
+ put := func (w * command ) {
63
+ binary .BigEndian .PutUint64 (long , w .index )
64
+ buf = append (buf , w .code )
65
+ buf = append (buf , long ... )
66
+ }
67
+
68
+ for t := range ticker .C {
69
+ _ = t
70
+ b := 0
71
+
72
+ drain:
73
+ for b + 9 < bufsize {
74
+ select {
75
+ case write := <- writes :
76
+ put (& write )
77
+ b += 9
78
+ default :
79
+ break drain
80
+ }
81
+ }
82
+
83
+ if b > 0 {
84
+ // write a chunk
85
+ if _ , err := w .Write (buf [:b ]); err != nil {
86
+ panic (err )
87
+ }
88
+
89
+ if err = w .Flush (); err != nil {
90
+ panic (err )
91
+ }
92
+ }
93
+ }
94
+ }
95
+
96
+ func New (length uint64 , binlogfile string ) * BitSet {
16
97
size := getSize (length )
17
- return & BitSet {length , make ([]uint64 , size )}
98
+ ret := & BitSet {
99
+ length ,
100
+ make ([]uint64 , size ),
101
+ make (chan command , channelsize ),
102
+ binlogfile ,
103
+ }
104
+ ret .writes <- command {new , length }
105
+ go flush (binlogfile , ret .writes )
106
+ return ret
18
107
}
19
108
20
109
func getIndex (pos uint64 ) (q uint64 , r uint ) {
@@ -37,19 +126,22 @@ func (b *BitSet) Set(pos uint64) bool {
37
126
current := b .Get (pos )
38
127
q , r := getIndex (pos )
39
128
b .bits [q ] |= (1 << r )
129
+ b .writes <- command {set , pos }
40
130
return current
41
131
}
42
132
43
133
func (b * BitSet ) Clear (pos uint64 ) bool {
44
134
current := b .Get (pos )
45
135
q , r := getIndex (pos )
46
136
b .bits [q ] &= ^ (1 << r )
137
+ b .writes <- command {clear , pos }
47
138
return current
48
139
}
49
140
50
141
func (b * BitSet ) Flip (pos uint64 ) bool {
51
142
current := b .Get (pos )
52
143
q , r := getIndex (pos )
53
144
b .bits [q ] ^= (1 << r )
145
+ b .writes <- command {flip , pos }
54
146
return current
55
147
}
0 commit comments