higgs 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/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:
|