daybreak 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/daybreak/db.rb CHANGED
@@ -28,8 +28,7 @@ module Daybreak
28
28
  # @param [Boolean] sync if true, sync this value immediately
29
29
  def []=(key, value, sync = false)
30
30
  key = key.to_s
31
- @writer.write(Record.new(key, serialize(value)))
32
- flush! if sync
31
+ write key, value, sync
33
32
  @table[key] = value
34
33
  end
35
34
  alias_method :set, :"[]="
@@ -41,6 +40,21 @@ module Daybreak
41
40
  set key, value, true
42
41
  end
43
42
 
43
+ # Delete a key from the database
44
+ # @param [#to_s] key the key of the storage slot in the database
45
+ # @param [Boolean] sync if true, sync this deletion immediately
46
+ def delete(key, sync = false)
47
+ key = key.to_s
48
+ write key, '', sync, true
49
+ @table.delete key
50
+ end
51
+
52
+ # delete! immediately deletes the key on disk.
53
+ # @param [#to_s] key the key of the storage slot in the database
54
+ def delete!(key)
55
+ delete key, true
56
+ end
57
+
44
58
  # Retrieve a value at key from the database. If the default value was specified
45
59
  # when this database was created, that value will be set and returned. Aliased
46
60
  # as <tt>get</tt>.
@@ -112,6 +126,7 @@ module Daybreak
112
126
  @writer.truncate!
113
127
  reset!
114
128
  end
129
+ alias_method :clear, :empty!
115
130
 
116
131
  # Force all queued commits to be written to disk.
117
132
  def flush!
@@ -138,7 +153,7 @@ module Daybreak
138
153
  copy_db = self.class.new tmp_file.path
139
154
 
140
155
  # Copy the database key by key into the temporary table
141
- each do |key, i|
156
+ each do |key|
142
157
  copy_db.set(key, get(key))
143
158
  end
144
159
  copy_db.close!
@@ -161,8 +176,19 @@ module Daybreak
161
176
  # call this again.
162
177
  def read!
163
178
  @reader.read do |record|
164
- @table[record.key] = parse record.data
179
+ if record.deleted?
180
+ @table.delete record.key
181
+ else
182
+ @table[record.key] = parse(record.data)
183
+ end
165
184
  end
166
185
  end
186
+
187
+ private
188
+
189
+ def write(key, value, sync = false, delete = false)
190
+ @writer.write(Record.new(key, serialize(value), delete))
191
+ flush! if sync
192
+ end
167
193
  end
168
194
  end
@@ -9,11 +9,19 @@ module Daybreak
9
9
  class CorruptDataError < Exception; end
10
10
  include Locking
11
11
 
12
+ # The mask a record uses to check for deletion.
13
+ DELETION_MASK = (1 << 31)
14
+
12
15
  attr_accessor :key, :data
13
16
 
14
- def initialize(key = nil, data = nil)
17
+ def initialize(key = nil, data = nil, deleted = false)
15
18
  @key = key
16
19
  @data = data
20
+ if deleted
21
+ @deleted = DELETION_MASK
22
+ else
23
+ @deleted = 0
24
+ end
17
25
  end
18
26
 
19
27
  # Read a record from an open io source, check the CRC, and set <tt>@key</tt>
@@ -21,8 +29,8 @@ module Daybreak
21
29
  # @param [#read] io an IO instance to read from
22
30
  def read(io)
23
31
  lock io do
24
- @key = read_bytes(io)
25
- @data = read_bytes(io)
32
+ @key = read_key(io)
33
+ @data = read_data(io)
26
34
  crc = io.read(4)
27
35
  raise CorruptDataError, "CRC mismatch #{crc} should be #{crc_string}" unless crc == crc_string
28
36
  end
@@ -42,24 +50,38 @@ module Daybreak
42
50
  new.read(io)
43
51
  end
44
52
 
53
+ def deleted?
54
+ @deleted > 0
55
+ end
56
+
45
57
  private
46
58
 
47
59
  def byte_string
48
- @byte_string ||= part(@key) + part(@data)
60
+ @byte_string ||= part(@key, @key.bytesize + @deleted) + part(@data, @data.bytesize)
49
61
  end
50
62
 
51
63
  def crc_string
52
64
  [Zlib.crc32(byte_string, 0)].pack('N')
53
65
  end
54
66
 
55
- def read_bytes(io)
67
+ def read_data(io)
68
+ io.read read32(io)
69
+ end
70
+
71
+ def read_key(io)
72
+ masked = read32 io
73
+ @deleted = masked & DELETION_MASK
74
+ length = masked & (DELETION_MASK - 1)
75
+ io.read length
76
+ end
77
+
78
+ def read32(io)
56
79
  raw = io.read(4)
57
- length = raw.unpack('N')[0]
58
- io.read(length)
80
+ raw.unpack('N')[0]
59
81
  end
60
82
 
61
- def part(data)
62
- [data.bytesize].pack('N') + data
83
+ def part(data, length)
84
+ [length].pack('N') + data
63
85
  end
64
86
  end
65
87
  end
@@ -1,4 +1,4 @@
1
1
  module Daybreak
2
2
  # Updated using SemVer
3
- VERSION = "0.0.4"
3
+ VERSION = "0.1.0"
4
4
  end
data/test/test.rb CHANGED
@@ -77,6 +77,40 @@ describe "database functions" do
77
77
  assert_equal nil, db2['19']
78
78
  end
79
79
 
80
+ it "should compact subclassed dbs" do
81
+ class StringDB < Daybreak::DB
82
+ def serialize(it)
83
+ it.to_s
84
+ end
85
+
86
+ def parse(it)
87
+ it
88
+ end
89
+ end
90
+
91
+ db = StringDB.new 'string.db'
92
+ db[1] = 'one'
93
+ db[2] = 'two'
94
+ db.delete 2
95
+ db.compact!
96
+ assert_equal db[1], 'one'
97
+ assert_equal db[2], nil
98
+ db.empty!
99
+ db.close!
100
+ end
101
+
102
+ it "should handle deletions" do
103
+ @db[1] = 'one'
104
+ @db[2] = 'two'
105
+ @db.delete 'two'
106
+ assert !@db.has_key?('two')
107
+ assert_equal @db['two'], nil
108
+
109
+ db2 = Daybreak::DB.new DB_PATH
110
+ assert !db2.has_key?('two')
111
+ assert_equal db2['two'], nil
112
+ end
113
+
80
114
  after do
81
115
  @db.empty!
82
116
  @db.close!
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: daybreak
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-27 00:00:00.000000000 Z
12
+ date: 2013-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest