higgs 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +208 -0
- data/LICENSE +26 -0
- data/README +2 -0
- data/Rakefile +75 -0
- data/bin/higgs_backup +67 -0
- data/bin/higgs_dump_index +43 -0
- data/bin/higgs_dump_jlog +42 -0
- data/bin/higgs_verify +37 -0
- data/lib/cgi/session/higgs.rb +72 -0
- data/lib/higgs/block.rb +192 -0
- data/lib/higgs/cache.rb +117 -0
- data/lib/higgs/dbm.rb +55 -0
- data/lib/higgs/exceptions.rb +31 -0
- data/lib/higgs/flock.rb +77 -0
- data/lib/higgs/index.rb +164 -0
- data/lib/higgs/jlog.rb +159 -0
- data/lib/higgs/lock.rb +189 -0
- data/lib/higgs/storage.rb +1086 -0
- data/lib/higgs/store.rb +228 -0
- data/lib/higgs/tar.rb +390 -0
- data/lib/higgs/thread.rb +370 -0
- data/lib/higgs/tman.rb +513 -0
- data/lib/higgs/utils/bman.rb +285 -0
- data/lib/higgs/utils.rb +22 -0
- data/lib/higgs/version.rb +21 -0
- data/lib/higgs.rb +59 -0
- data/misc/cache_bench/cache_bench.rb +43 -0
- data/misc/dbm_bench/.strc +8 -0
- data/misc/dbm_bench/Rakefile +78 -0
- data/misc/dbm_bench/dbm_multi_thread.rb +199 -0
- data/misc/dbm_bench/dbm_rnd_delete.rb +43 -0
- data/misc/dbm_bench/dbm_rnd_read.rb +44 -0
- data/misc/dbm_bench/dbm_rnd_update.rb +44 -0
- data/misc/dbm_bench/dbm_seq_read.rb +45 -0
- data/misc/dbm_bench/dbm_seq_write.rb +44 -0
- data/misc/dbm_bench/st_verify.rb +28 -0
- data/misc/io_bench/cksum_bench.rb +48 -0
- data/misc/io_bench/jlog_bench.rb +71 -0
- data/misc/io_bench/write_bench.rb +128 -0
- data/misc/thread_bench/lock_bench.rb +132 -0
- data/mkrdoc.rb +8 -0
- data/rdoc.yml +13 -0
- data/sample/count.rb +60 -0
- data/sample/dbmtest.rb +38 -0
- data/test/Rakefile +45 -0
- data/test/run.rb +32 -0
- data/test/test_block.rb +163 -0
- data/test/test_cache.rb +214 -0
- data/test/test_cgi_session.rb +142 -0
- data/test/test_flock.rb +162 -0
- data/test/test_index.rb +258 -0
- data/test/test_jlog.rb +180 -0
- data/test/test_lock.rb +320 -0
- data/test/test_online_backup.rb +169 -0
- data/test/test_storage.rb +439 -0
- data/test/test_storage_conf.rb +202 -0
- data/test/test_storage_init_opts.rb +89 -0
- data/test/test_store.rb +211 -0
- data/test/test_tar.rb +432 -0
- data/test/test_thread.rb +541 -0
- data/test/test_tman.rb +875 -0
- data/test/test_tman_init_opts.rb +56 -0
- data/test/test_utils_bman.rb +234 -0
- metadata +115 -0
data/lib/higgs/block.rb
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
# = block read/write
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'higgs/exceptions'
|
12
|
+
|
13
|
+
module Higgs
|
14
|
+
# block header format
|
15
|
+
#
|
16
|
+
# 0..15 : Z16 : magic symbol
|
17
|
+
# 16..19 : V : body length
|
18
|
+
# 20..31 : x12 : (reserved)
|
19
|
+
# 32..33 : v : format version
|
20
|
+
# 34..35 : x2 : (reserved)
|
21
|
+
# 36..37 : v : head cksum
|
22
|
+
# 38..39 : x2 : (reserved)
|
23
|
+
# 40..55 : Z16 : body hash type
|
24
|
+
# 56..57 : v : body hash length
|
25
|
+
# 58..71 : x14 : (reserved)
|
26
|
+
# 73..511 : a440 : body hash binary
|
27
|
+
#
|
28
|
+
module Block
|
29
|
+
# for ident(1)
|
30
|
+
CVS_ID = '$Id: block.rb 559 2007-09-25 15:20:20Z toki $'
|
31
|
+
|
32
|
+
include Exceptions
|
33
|
+
|
34
|
+
class BrokenError < HiggsError
|
35
|
+
end
|
36
|
+
|
37
|
+
BLOCK_SIZE = 512
|
38
|
+
|
39
|
+
FMT_VERSION = 0x00_00
|
40
|
+
HEAD_CKSUM_BITS = 16
|
41
|
+
HEAD_CKSUM_POS = 36..37
|
42
|
+
HEAD_CKSUM_FMT = 'v'
|
43
|
+
|
44
|
+
BODY_HASH = {}
|
45
|
+
[ [ :SUM16, proc{|s| s.sum(16).to_s }, nil ],
|
46
|
+
[ :MD5, proc{|s| Digest::MD5.digest(s) }, 'digest/md5' ],
|
47
|
+
[ :RMD160, proc{|s| Digest::RMD160.digest(s) }, 'digest/rmd160' ],
|
48
|
+
[ :SHA1, proc{|s| Digest::SHA1.digest(s) }, 'digest/sha1' ],
|
49
|
+
[ :SHA256, proc{|s| Digest::SHA256.digest(s) }, 'digest/sha2' ],
|
50
|
+
[ :SHA384, proc{|s| Digest::SHA384.digest(s) }, 'digest/sha2' ],
|
51
|
+
[ :SHA512, proc{|s| Digest::SHA512.digest(s) }, 'digest/sha2' ]
|
52
|
+
].each do |hash_symbol, hash_proc, hash_lib|
|
53
|
+
if (hash_lib) then
|
54
|
+
begin
|
55
|
+
require(hash_lib)
|
56
|
+
rescue LoadError
|
57
|
+
next
|
58
|
+
end
|
59
|
+
end
|
60
|
+
BODY_HASH[hash_symbol] = hash_proc
|
61
|
+
end
|
62
|
+
|
63
|
+
BODY_HASH_BIN = {}
|
64
|
+
BODY_HASH.each do |hash_symbol, hash_proc|
|
65
|
+
BODY_HASH_BIN[hash_symbol.to_s] = hash_proc
|
66
|
+
end
|
67
|
+
|
68
|
+
HEAD_FMT = [
|
69
|
+
'Z16', # magic symbol
|
70
|
+
'V', # body length
|
71
|
+
'x12', # (reserved)
|
72
|
+
'v', # format version
|
73
|
+
'x2', # (reserved)
|
74
|
+
HEAD_CKSUM_FMT, # head cksum
|
75
|
+
'x2', # (reserved)
|
76
|
+
'Z16', # body hash type
|
77
|
+
'v', # body hash length
|
78
|
+
'x14', # (reserved)
|
79
|
+
'a440' # body hash binary
|
80
|
+
].join('')
|
81
|
+
|
82
|
+
def padding_size(bytes)
|
83
|
+
r = bytes % BLOCK_SIZE
|
84
|
+
(r > 0) ? BLOCK_SIZE - r : 0
|
85
|
+
end
|
86
|
+
module_function :padding_size
|
87
|
+
|
88
|
+
def head_read(io, magic_symbol)
|
89
|
+
head_block = io.read(BLOCK_SIZE) or return
|
90
|
+
if (head_block.size != BLOCK_SIZE) then
|
91
|
+
raise BrokenError, 'short read'
|
92
|
+
end
|
93
|
+
|
94
|
+
_magic_symbol, body_len, fmt_version, head_cksum,
|
95
|
+
body_hash_type, body_hash_len, body_hash_bucket = head_block.unpack(HEAD_FMT)
|
96
|
+
|
97
|
+
head_block[HEAD_CKSUM_POS] = "\000\000"
|
98
|
+
if (head_block.sum(HEAD_CKSUM_BITS) != head_cksum) then
|
99
|
+
raise BrokenError, 'broken head block'
|
100
|
+
end
|
101
|
+
|
102
|
+
if (_magic_symbol != magic_symbol) then
|
103
|
+
raise BrokenError, "unknown magic symbol: #{_magic_symbol}"
|
104
|
+
end
|
105
|
+
|
106
|
+
if (fmt_version != FMT_VERSION) then
|
107
|
+
raise BrokenError, format('unknown format version: 0x%04F', fmt_version)
|
108
|
+
end
|
109
|
+
|
110
|
+
body_hash_bin = body_hash_bucket[0, body_hash_len]
|
111
|
+
|
112
|
+
return body_len, body_hash_type, body_hash_bin
|
113
|
+
end
|
114
|
+
module_function :head_read
|
115
|
+
|
116
|
+
def head_write(io, magic_symbol, body_len, body_hash_type, body_hash_bin)
|
117
|
+
head_block = [
|
118
|
+
magic_symbol,
|
119
|
+
body_len,
|
120
|
+
FMT_VERSION,
|
121
|
+
0,
|
122
|
+
body_hash_type,
|
123
|
+
body_hash_bin.length,
|
124
|
+
body_hash_bin
|
125
|
+
].pack(HEAD_FMT)
|
126
|
+
|
127
|
+
head_cksum = head_block.sum(HEAD_CKSUM_BITS)
|
128
|
+
head_block[HEAD_CKSUM_POS] = [ head_cksum ].pack(HEAD_CKSUM_FMT)
|
129
|
+
|
130
|
+
bytes = io.write(head_block)
|
131
|
+
if (bytes != head_block.size) then
|
132
|
+
raise BrokenError, 'short write'
|
133
|
+
end
|
134
|
+
bytes
|
135
|
+
end
|
136
|
+
module_function :head_write
|
137
|
+
|
138
|
+
def block_read(io, magic_symbol)
|
139
|
+
body_len, body_hash_type, body_hash_bin = head_read(io, magic_symbol)
|
140
|
+
unless (body_len) then
|
141
|
+
return
|
142
|
+
end
|
143
|
+
|
144
|
+
body = io.read(body_len) or raise BrokenError, 'unexpected EOF'
|
145
|
+
if (body.length != body_len) then
|
146
|
+
raise BrokenError, 'short read'
|
147
|
+
end
|
148
|
+
|
149
|
+
unless (hash_proc = BODY_HASH_BIN[body_hash_type]) then
|
150
|
+
raise BrokenError, "unknown body hash type: #{body_hash_type}"
|
151
|
+
end
|
152
|
+
|
153
|
+
if (hash_proc.call(body) != body_hash_bin) then
|
154
|
+
raise BrokenError, 'body hash error'
|
155
|
+
end
|
156
|
+
|
157
|
+
padding_size = padding_size(body_len)
|
158
|
+
padding = io.read(padding_size) or raise BrokenError, 'unexpected EOF'
|
159
|
+
if (padding.size != padding_size) then
|
160
|
+
raise BrokenError, 'short read'
|
161
|
+
end
|
162
|
+
|
163
|
+
body
|
164
|
+
end
|
165
|
+
module_function :block_read
|
166
|
+
|
167
|
+
def block_write(io, magic_symbol, body, body_hash_type=:MD5)
|
168
|
+
hash_proc = BODY_HASH[body_hash_type.to_sym] or "unknown body hash type: #{body_hash_type}"
|
169
|
+
body_hash = hash_proc.call(body)
|
170
|
+
head_write(io, magic_symbol, body.length, body_hash_type.to_s, body_hash)
|
171
|
+
|
172
|
+
bytes = io.write(body)
|
173
|
+
if (bytes != body.size) then
|
174
|
+
raise BrokenError, 'short write'
|
175
|
+
end
|
176
|
+
|
177
|
+
padding_size = padding_size(body.length)
|
178
|
+
bytes = io.write("\0" * padding_size)
|
179
|
+
if (bytes != padding_size) then
|
180
|
+
raise BrokenError, 'short write'
|
181
|
+
end
|
182
|
+
|
183
|
+
body.size + padding_size
|
184
|
+
end
|
185
|
+
module_function :block_write
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Local Variables:
|
190
|
+
# mode: Ruby
|
191
|
+
# indent-tabs-mode: nil
|
192
|
+
# End:
|
data/lib/higgs/cache.rb
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# = cache utilities
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'forwardable'
|
12
|
+
require 'higgs/thread'
|
13
|
+
require 'thread'
|
14
|
+
|
15
|
+
module Higgs
|
16
|
+
# = cache by Least Recently Used strategy
|
17
|
+
class LRUCache
|
18
|
+
# for ident(1)
|
19
|
+
CVS_ID = '$Id: cache.rb 559 2007-09-25 15:20:20Z toki $'
|
20
|
+
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
def initialize(limit_size=1000)
|
24
|
+
@limit_size = limit_size
|
25
|
+
@count = 0
|
26
|
+
@cache = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :limit_size
|
30
|
+
def_delegator :@cache, :keys
|
31
|
+
def_delegator :@cache, :key?
|
32
|
+
alias has_key? key?
|
33
|
+
alias include? key?
|
34
|
+
|
35
|
+
def [](key)
|
36
|
+
if (cached_pair = @cache[key]) then
|
37
|
+
cached_pair[1] = @count
|
38
|
+
@count = @count.succ
|
39
|
+
return cached_pair[0]
|
40
|
+
end
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def []=(key, value)
|
45
|
+
if (cached_pair = @cache[key]) then
|
46
|
+
cached_pair[1] = @count
|
47
|
+
else
|
48
|
+
@cache[key] = [ value, @count ]
|
49
|
+
end
|
50
|
+
@count = @count.succ
|
51
|
+
if (@cache.size > @limit_size) then
|
52
|
+
purge_old_cache
|
53
|
+
end
|
54
|
+
value
|
55
|
+
end
|
56
|
+
|
57
|
+
def purge_old_cache
|
58
|
+
c_list = @cache.map{|key, (value, c)| c }
|
59
|
+
c_list.sort!
|
60
|
+
threshold = c_list[c_list.size / 2]
|
61
|
+
@cache.delete_if{|key, (value, c)| c < threshold }
|
62
|
+
end
|
63
|
+
private :purge_old_cache
|
64
|
+
|
65
|
+
def delete(key)
|
66
|
+
if (cached_pair = @cache.delete(key)) then
|
67
|
+
return cached_pair[0]
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# = multi-thread safe cache
|
74
|
+
class SharedWorkCache
|
75
|
+
# for ident(1)
|
76
|
+
CVS_ID = '$Id: cache.rb 559 2007-09-25 15:20:20Z toki $'
|
77
|
+
|
78
|
+
def initialize(cache={}, &work)
|
79
|
+
unless (work) then
|
80
|
+
raise 'required work block'
|
81
|
+
end
|
82
|
+
@work = work
|
83
|
+
@lock = Mutex.new
|
84
|
+
@cache = cache
|
85
|
+
end
|
86
|
+
|
87
|
+
def fetch_work(key)
|
88
|
+
@lock.synchronize{
|
89
|
+
if (@cache.key? key) then
|
90
|
+
@cache[key]
|
91
|
+
else
|
92
|
+
@cache[key] = SharedWork.new{ @work.call(key) }
|
93
|
+
end
|
94
|
+
}
|
95
|
+
end
|
96
|
+
private :fetch_work
|
97
|
+
|
98
|
+
def [](key)
|
99
|
+
fetch_work(key).result
|
100
|
+
end
|
101
|
+
|
102
|
+
def []=(key, value)
|
103
|
+
work = fetch_work(key)
|
104
|
+
work.result = value
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete(key)
|
108
|
+
r = @lock.synchronize{ @cache.delete(key) }
|
109
|
+
r ? true : false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Local Variables:
|
115
|
+
# mode: Ruby
|
116
|
+
# indent-tabs-mode: nil
|
117
|
+
# End:
|
data/lib/higgs/dbm.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# = storage like dbm
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'higgs/storage'
|
12
|
+
require 'higgs/tman'
|
13
|
+
|
14
|
+
module Higgs
|
15
|
+
# = storage like dbm
|
16
|
+
# == sample script
|
17
|
+
# sample/dbmtest.rb
|
18
|
+
# :include: sample/dbmtest.rb
|
19
|
+
#
|
20
|
+
class DBM
|
21
|
+
# for ident(1)
|
22
|
+
CVS_ID = '$Id: dbm.rb 559 2007-09-25 15:20:20Z toki $'
|
23
|
+
|
24
|
+
include Storage::Export
|
25
|
+
include TransactionManager::Export
|
26
|
+
|
27
|
+
DECODE = proc{|r| r }
|
28
|
+
ENCODE = proc{|w| w }
|
29
|
+
|
30
|
+
# <tt>name</tt> is a storage name and see Higgs::Storage.new for
|
31
|
+
# detail. see Higgs::Storage::InitOptions and
|
32
|
+
# Higgs::TransactionManager::InitOptions for <tt>options</tt>.
|
33
|
+
def initialize(name, options={})
|
34
|
+
@storage = Storage.new(name, options)
|
35
|
+
options[:decode] = DECODE
|
36
|
+
options[:encode] = ENCODE
|
37
|
+
@tman = TransactionManager.new(@storage, options)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.open(*args)
|
41
|
+
dbm = new(*args)
|
42
|
+
begin
|
43
|
+
r = yield(dbm)
|
44
|
+
ensure
|
45
|
+
dbm.shutdown
|
46
|
+
end
|
47
|
+
r
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Local Variables:
|
53
|
+
# mode: Ruby
|
54
|
+
# indent-tabs-mode: nil
|
55
|
+
# End:
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# = exceptions
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
module Higgs
|
12
|
+
module Exceptions
|
13
|
+
# for ident(1)
|
14
|
+
CVS_ID = '$Id: exceptions.rb 559 2007-09-25 15:20:20Z toki $'
|
15
|
+
|
16
|
+
class HiggsError < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
class HiggsException < Exception
|
20
|
+
end
|
21
|
+
|
22
|
+
class ShutdownException < HiggsException
|
23
|
+
end
|
24
|
+
end
|
25
|
+
include Exceptions
|
26
|
+
end
|
27
|
+
|
28
|
+
# Local Variables:
|
29
|
+
# mode: Ruby
|
30
|
+
# indent-tabs-mode: nil
|
31
|
+
# End:
|
data/lib/higgs/flock.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# = file lock
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
module Higgs
|
12
|
+
class FileLock
|
13
|
+
# for ident(1)
|
14
|
+
CVS_ID = '$Id: flock.rb 559 2007-09-25 15:20:20Z toki $'
|
15
|
+
|
16
|
+
def initialize(path, read_only=false)
|
17
|
+
@path = path
|
18
|
+
@read_only = read_only
|
19
|
+
rdwr_mode = (@read_only) ? File::RDONLY : File::RDWR
|
20
|
+
begin
|
21
|
+
@f = File.open(path, rdwr_mode | File::CREAT | File::EXCL, 0660)
|
22
|
+
rescue Errno::EEXIST
|
23
|
+
@f = File.open(path, rdwr_mode, 0660)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
attr_reader :read_only
|
28
|
+
|
29
|
+
def read_lock
|
30
|
+
@f.flock(File::LOCK_SH)
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def write_lock
|
35
|
+
if (@read_only) then
|
36
|
+
raise 'read only'
|
37
|
+
end
|
38
|
+
@f.flock(File::LOCK_EX)
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def unlock
|
43
|
+
@f.flock(File::LOCK_UN)
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
|
47
|
+
def close
|
48
|
+
unlock
|
49
|
+
@f.close
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def synchronize(mode=:EX)
|
54
|
+
case (mode)
|
55
|
+
when :SH
|
56
|
+
read_lock
|
57
|
+
when :EX
|
58
|
+
write_lock
|
59
|
+
else
|
60
|
+
raise ArgumentError, "unknown lock mode: #{mode}"
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
r = yield
|
65
|
+
ensure
|
66
|
+
unlock
|
67
|
+
end
|
68
|
+
|
69
|
+
r
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Local Variables:
|
75
|
+
# mode: Ruby
|
76
|
+
# indent-tabs-mode: nil
|
77
|
+
# End:
|
data/lib/higgs/index.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
# = storage index
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'forwardable'
|
12
|
+
require 'higgs/block'
|
13
|
+
|
14
|
+
module Higgs
|
15
|
+
# = storage index
|
16
|
+
class Index
|
17
|
+
# for ident(1)
|
18
|
+
CVS_ID = '$Id: index.rb 559 2007-09-25 15:20:20Z toki $'
|
19
|
+
|
20
|
+
extend Forwardable
|
21
|
+
include Block
|
22
|
+
|
23
|
+
MAGIC_SYMBOL = 'HIGGS_INDEX'
|
24
|
+
MAJOR_VERSION = 0
|
25
|
+
MINOR_VERSION = 1
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@change_number = 0
|
29
|
+
@eoa = 0
|
30
|
+
@free_lists = {}
|
31
|
+
@index = {}
|
32
|
+
@identities = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :change_number
|
36
|
+
attr_accessor :eoa
|
37
|
+
|
38
|
+
def succ!
|
39
|
+
@change_number = @change_number.succ
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def free_fetch(size)
|
44
|
+
@free_lists[size].shift if (@free_lists.key? size)
|
45
|
+
end
|
46
|
+
|
47
|
+
def free_fetch_at(pos, size)
|
48
|
+
@free_lists[size].delete(pos) or raise "not found free region: (#{pos},#{size})"
|
49
|
+
end
|
50
|
+
|
51
|
+
def free_store(pos, size)
|
52
|
+
@free_lists[size] = [] unless (@free_lists.key? size)
|
53
|
+
@free_lists[size] << pos
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def_delegator :@index, :key?
|
58
|
+
def_delegator :@index, :keys
|
59
|
+
|
60
|
+
def identity(key)
|
61
|
+
i = @index[key] and i[0]
|
62
|
+
end
|
63
|
+
|
64
|
+
def [](key)
|
65
|
+
i = @index[key] and i[1]
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.create_id(key, identities)
|
69
|
+
id = key.to_s
|
70
|
+
if (identities.key? id) then
|
71
|
+
id += '.a'
|
72
|
+
id.succ! while (identities.key? id)
|
73
|
+
end
|
74
|
+
id
|
75
|
+
end
|
76
|
+
|
77
|
+
def []=(key, value)
|
78
|
+
delete(key)
|
79
|
+
id = Index.create_id(key, @identities)
|
80
|
+
@identities[id] = key
|
81
|
+
@index[key] = [ id, value ]
|
82
|
+
value
|
83
|
+
end
|
84
|
+
|
85
|
+
def delete(key)
|
86
|
+
id, value = @index.delete(key)
|
87
|
+
@identities.delete(id) if id
|
88
|
+
value
|
89
|
+
end
|
90
|
+
|
91
|
+
def each_key
|
92
|
+
@index.each_key do |key|
|
93
|
+
yield(key)
|
94
|
+
end
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_h
|
99
|
+
{ :version => [ MAJOR_VERSION, MINOR_VERSION ],
|
100
|
+
:change_number => @change_number,
|
101
|
+
:eoa => @eoa,
|
102
|
+
:free_lists => @free_lists,
|
103
|
+
:index => @index,
|
104
|
+
:identities => @identities
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def migration_0_0_to_0_1(index_data)
|
109
|
+
if ((index_data[:version] <=> [ 0, 0 ]) > 0) then
|
110
|
+
return
|
111
|
+
end
|
112
|
+
if ((index_data[:version] <=> [ 0, 0 ]) < 0) then
|
113
|
+
raise "unexpected index format version: #{index_data[:version].join('.')}"
|
114
|
+
end
|
115
|
+
|
116
|
+
index = index_data[:index]
|
117
|
+
identities = index_data[:identities] = {}
|
118
|
+
for key in index.keys
|
119
|
+
id = Index.create_id(key, identities)
|
120
|
+
identities[id] = key
|
121
|
+
value = index[key]
|
122
|
+
index[key] = [ id, value ]
|
123
|
+
end
|
124
|
+
index_data[:version] = [ 0, 1 ]
|
125
|
+
|
126
|
+
index_data
|
127
|
+
end
|
128
|
+
private :migration_0_0_to_0_1
|
129
|
+
|
130
|
+
def save(path)
|
131
|
+
tmp_path = "#{path}.tmp.#{$$}"
|
132
|
+
File.open(tmp_path, File::WRONLY | File::CREAT | File::TRUNC, 0660) {|f|
|
133
|
+
f.binmode
|
134
|
+
index_data = self.to_h
|
135
|
+
block_write(f, MAGIC_SYMBOL, Marshal.dump(index_data))
|
136
|
+
f.fsync
|
137
|
+
}
|
138
|
+
File.rename(tmp_path, path)
|
139
|
+
self
|
140
|
+
end
|
141
|
+
|
142
|
+
def load(path)
|
143
|
+
File.open(path, 'r') {|f|
|
144
|
+
f.binmode
|
145
|
+
index_data = Marshal.load(block_read(f, MAGIC_SYMBOL))
|
146
|
+
migration_0_0_to_0_1(index_data)
|
147
|
+
if ((index_data[:version] <=> [ MAJOR_VERSION, MINOR_VERSION ]) > 0) then
|
148
|
+
raise "unsupported version: #{index_data[:version].join('.')}"
|
149
|
+
end
|
150
|
+
@change_number = index_data[:change_number]
|
151
|
+
@eoa = index_data[:eoa]
|
152
|
+
@free_lists = index_data[:free_lists]
|
153
|
+
@index = index_data[:index]
|
154
|
+
@identities = index_data[:identities]
|
155
|
+
}
|
156
|
+
self
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Local Variables:
|
162
|
+
# mode: Ruby
|
163
|
+
# indent-tabs-mode: nil
|
164
|
+
# End:
|