daybreak 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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