strokedb 0.0.2.1 → 0.0.2.2
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/README +18 -20
- data/bench.html +4001 -0
- data/bin/strokedb +14 -0
- data/examples/movies.rb +105 -0
- data/examples/movies2.rb +97 -0
- data/examples/strokewiki/README +28 -0
- data/examples/strokewiki/view/edit.xhtml +27 -0
- data/examples/strokewiki/view/new.xhtml +26 -0
- data/examples/strokewiki/view/pages.xhtml +27 -0
- data/examples/strokewiki/view/show.xhtml +40 -0
- data/examples/strokewiki/view/versions.xhtml +25 -0
- data/examples/strokewiki/wiki.rb +106 -0
- data/examples/todo.rb +92 -0
- data/lib/strokedb.rb +85 -0
- data/lib/{config → strokedb}/config.rb +14 -9
- data/lib/strokedb/console.rb +87 -0
- data/lib/strokedb/core_ext.rb +10 -0
- data/lib/{util/ext → strokedb/core_ext}/blank.rb +1 -1
- data/lib/{util/ext → strokedb/core_ext}/enumerable.rb +0 -0
- data/lib/{util/ext → strokedb/core_ext}/fixnum.rb +0 -0
- data/lib/strokedb/core_ext/float.rb +4 -0
- data/lib/{util/ext → strokedb/core_ext}/hash.rb +0 -0
- data/lib/strokedb/core_ext/infinity.rb +33 -0
- data/lib/strokedb/core_ext/kernel.rb +41 -0
- data/lib/strokedb/core_ext/object.rb +16 -0
- data/lib/{util/ext → strokedb/core_ext}/string.rb +28 -1
- data/lib/strokedb/core_ext/symbol.rb +13 -0
- data/lib/strokedb/data_structures.rb +5 -0
- data/lib/strokedb/data_structures/chunked_skiplist.rb +123 -0
- data/lib/{data_structures → strokedb/data_structures}/inverted_list.rb +0 -0
- data/lib/{data_structures → strokedb/data_structures}/point_query.rb +0 -0
- data/lib/strokedb/data_structures/simple_skiplist.rb +350 -0
- data/lib/{data_structures → strokedb/data_structures}/skiplist.rb +1 -1
- data/lib/{document → strokedb}/document.rb +180 -71
- data/lib/{document → strokedb/document}/callback.rb +0 -0
- data/lib/{document → strokedb/document}/delete.rb +2 -2
- data/lib/strokedb/document/dsl.rb +4 -0
- data/lib/{document → strokedb/document/dsl}/associations.rb +0 -0
- data/lib/{document → strokedb/document/dsl}/coercions.rb +0 -0
- data/lib/strokedb/document/dsl/meta_dsl.rb +7 -0
- data/lib/{document → strokedb/document/dsl}/validations.rb +26 -21
- data/lib/{document → strokedb/document/dsl}/virtualize.rb +0 -0
- data/lib/{document → strokedb/document}/meta.rb +92 -29
- data/lib/{document → strokedb/document}/slot.rb +17 -5
- data/lib/{document → strokedb/document}/util.rb +0 -0
- data/lib/{document → strokedb/document}/versions.rb +2 -2
- data/lib/strokedb/index.rb +2 -0
- data/lib/strokedb/nsurl.rb +24 -0
- data/lib/strokedb/store.rb +149 -0
- data/lib/strokedb/stores.rb +6 -0
- data/lib/{stores → strokedb/stores}/chainable_storage.rb +20 -14
- data/lib/strokedb/stores/file_storage.rb +118 -0
- data/lib/{stores/inverted_list_index → strokedb/stores}/inverted_list_file_storage.rb +50 -0
- data/lib/strokedb/stores/memory_storage.rb +80 -0
- data/lib/{stores → strokedb/stores}/remote_store.rb +10 -4
- data/lib/strokedb/sync.rb +4 -0
- data/lib/{sync → strokedb/sync}/chain_sync.rb +0 -0
- data/lib/{sync → strokedb/sync}/diff.rb +12 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/array.rb +1 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/default.rb +0 -0
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/hash.rb +1 -1
- data/lib/{sync/stroke_diff → strokedb/sync/diff}/string.rb +1 -1
- data/lib/{sync → strokedb/sync}/lamport_timestamp.rb +0 -0
- data/lib/{sync → strokedb/sync}/store_sync.rb +15 -7
- data/lib/strokedb/transaction.rb +78 -0
- data/lib/{util → strokedb}/util.rb +14 -7
- data/lib/strokedb/util/attach_dsl.rb +29 -0
- data/lib/{util → strokedb/util}/blankslate.rb +0 -0
- data/lib/strokedb/util/class_optimization.rb +93 -0
- data/lib/{util → strokedb/util}/inflect.rb +0 -0
- data/lib/strokedb/util/java_util.rb +13 -0
- data/lib/{util → strokedb/util}/lazy_array.rb +0 -0
- data/lib/{util → strokedb/util}/lazy_mapping_array.rb +4 -0
- data/lib/{util → strokedb/util}/lazy_mapping_hash.rb +0 -0
- data/lib/{util → strokedb/util}/serialization.rb +21 -0
- data/lib/strokedb/util/uuid.rb +159 -0
- data/lib/{util → strokedb/util}/xml.rb +0 -0
- data/lib/{view → strokedb}/view.rb +2 -2
- data/lib/strokedb/volumes.rb +5 -0
- data/lib/strokedb/volumes/archive_volume.rb +165 -0
- data/lib/strokedb/volumes/block_volume.rb +169 -0
- data/lib/strokedb/volumes/distributed_pointer.rb +43 -0
- data/lib/strokedb/volumes/fixed_length_skiplist_volume.rb +109 -0
- data/lib/strokedb/volumes/map_volume.rb +268 -0
- data/meta/MANIFEST +175 -0
- data/script/console +2 -70
- data/spec/integration/remote_store_spec.rb +70 -0
- data/spec/integration/search_spec.rb +76 -0
- data/spec/integration/spec_helper.rb +1 -0
- data/spec/lib/spec_helper.rb +1 -0
- data/spec/lib/strokedb/config_spec.rb +250 -0
- data/spec/lib/strokedb/core_ext/blank_spec.rb +20 -0
- data/spec/lib/strokedb/core_ext/extract_spec.rb +42 -0
- data/spec/lib/strokedb/core_ext/float_spec.rb +62 -0
- data/spec/lib/strokedb/core_ext/infinity_spec.rb +40 -0
- data/spec/lib/strokedb/core_ext/spec_helper.rb +1 -0
- data/spec/lib/strokedb/core_ext/string_spec.rb +25 -0
- data/spec/lib/strokedb/core_ext/symbol_spec.rb +8 -0
- data/spec/lib/strokedb/data_structures/chunked_skiplist_spec.rb +144 -0
- data/spec/lib/strokedb/data_structures/inverted_list_spec.rb +172 -0
- data/spec/lib/strokedb/data_structures/simple_skiplist_spec.rb +200 -0
- data/spec/lib/strokedb/data_structures/skiplist_spec.rb +253 -0
- data/spec/lib/strokedb/data_structures/spec_helper.rb +1 -0
- data/spec/lib/strokedb/document/associations_spec.rb +319 -0
- data/spec/lib/strokedb/document/callbacks_spec.rb +134 -0
- data/spec/lib/strokedb/document/coercions_spec.rb +110 -0
- data/spec/lib/strokedb/document/document_spec.rb +1063 -0
- data/spec/lib/strokedb/document/meta_meta_spec.rb +30 -0
- data/spec/lib/strokedb/document/meta_spec.rb +435 -0
- data/spec/lib/strokedb/document/metaslot_spec.rb +43 -0
- data/spec/lib/strokedb/document/slot_spec.rb +130 -0
- data/spec/lib/strokedb/document/spec_helper.rb +1 -0
- data/spec/lib/strokedb/document/validations_spec.rb +1081 -0
- data/spec/lib/strokedb/document/virtualize_spec.rb +80 -0
- data/spec/lib/strokedb/nsurl_spec.rb +73 -0
- data/spec/lib/strokedb/spec_helper.rb +1 -0
- data/spec/lib/strokedb/stores/chained_storages_spec.rb +116 -0
- data/spec/lib/strokedb/stores/spec_helper.rb +1 -0
- data/spec/lib/strokedb/stores/store_spec.rb +201 -0
- data/spec/lib/strokedb/stores/transaction_spec.rb +107 -0
- data/spec/lib/strokedb/sync/chain_sync_spec.rb +43 -0
- data/spec/lib/strokedb/sync/diff_spec.rb +111 -0
- data/spec/lib/strokedb/sync/lamport_timestamp_spec.rb +174 -0
- data/spec/lib/strokedb/sync/slot_diff_spec.rb +164 -0
- data/spec/lib/strokedb/sync/spec_helper.rb +1 -0
- data/spec/lib/strokedb/sync/store_sync_spec.rb +181 -0
- data/spec/lib/strokedb/sync/stroke_diff/array_spec.rb +97 -0
- data/spec/lib/strokedb/sync/stroke_diff/complex_spec.rb +58 -0
- data/spec/lib/strokedb/sync/stroke_diff/hash_spec.rb +144 -0
- data/spec/lib/strokedb/sync/stroke_diff/scalar_spec.rb +23 -0
- data/spec/lib/strokedb/sync/stroke_diff/spec_helper.rb +25 -0
- data/spec/lib/strokedb/sync/stroke_diff/string_spec.rb +61 -0
- data/spec/lib/strokedb/util/attach_dsl_spec.rb +45 -0
- data/spec/lib/strokedb/util/inflect_spec.rb +14 -0
- data/spec/lib/strokedb/util/lazy_array_spec.rb +157 -0
- data/spec/lib/strokedb/util/lazy_mapping_array_spec.rb +174 -0
- data/spec/lib/strokedb/util/lazy_mapping_hash_spec.rb +92 -0
- data/spec/lib/strokedb/util/spec_helper.rb +1 -0
- data/spec/lib/strokedb/util/uuid_spec.rb +46 -0
- data/spec/lib/strokedb/view_spec.rb +228 -0
- data/spec/lib/strokedb/volumes/archive_volume_spec.rb +105 -0
- data/spec/lib/strokedb/volumes/block_volume_spec.rb +100 -0
- data/spec/lib/strokedb/volumes/distributed_pointer_spec.rb +14 -0
- data/spec/lib/strokedb/volumes/fixed_length_skiplist_volume_spec.rb +177 -0
- data/spec/lib/strokedb/volumes/map_volume_spec.rb +172 -0
- data/spec/lib/strokedb/volumes/spec_helper.rb +1 -0
- data/spec/regression/docref_spec.rb +94 -0
- data/spec/regression/meta_spec.rb +23 -0
- data/spec/regression/spec_helper.rb +1 -0
- data/spec/regression/sync_spec.rb +36 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/temp/storages/TIMESTAMP +1 -0
- data/spec/temp/storages/UUID +1 -0
- data/spec/temp/storages/database-sync/TIMESTAMP +1 -0
- data/spec/temp/storages/database-sync/UUID +1 -0
- data/spec/temp/storages/database-sync/config +1 -0
- data/spec/temp/storages/database-sync/file/LAST +1 -0
- data/spec/temp/storages/database-sync/file/bd/f6/bdf675e5-8a7b-494e-97f2-f74a14ccd95d.av +0 -0
- data/spec/temp/storages/database-sync/file/uindex.wal +0 -0
- data/spec/temp/storages/database-sync/inverted_list_file/INVERTED_INDEX +1 -0
- data/spec/temp/storages/inverted_list_storage/INVERTED_INDEX +0 -0
- data/strokedb.gemspec +120 -0
- data/task/benchmark.task +9 -0
- data/task/ditz.task +30 -0
- data/task/echoe.rb +17 -0
- data/task/rcov.task +50 -0
- data/task/rdoc.task +10 -0
- data/task/rspec.task +0 -0
- data/vendor/java_inline.rb +106 -0
- data/vendor/rbmodexcl/mrimodexcl.rb +82 -0
- data/vendor/rbmodexcl/rbmodexcl.rb +5 -0
- data/vendor/rbmodexcl/rbxmodexcl.rb +48 -0
- data/vendor/rbmodexcl/spec/unextend_spec.rb +50 -0
- data/vendor/rbmodexcl/spec/uninclude_spec.rb +26 -0
- metadata +271 -79
- data/CONTRIBUTORS +0 -7
- data/CREDITS +0 -13
- data/bin/sdbc +0 -2
- data/lib/init.rb +0 -57
- data/lib/stores/inverted_list_index/inverted_list_index.rb +0 -49
- data/lib/stores/skiplist_store/chunk.rb +0 -119
- data/lib/stores/skiplist_store/chunk_storage.rb +0 -21
- data/lib/stores/skiplist_store/file_chunk_storage.rb +0 -44
- data/lib/stores/skiplist_store/memory_chunk_storage.rb +0 -37
- data/lib/stores/skiplist_store/skiplist_store.rb +0 -217
- data/lib/stores/store.rb +0 -5
- data/lib/sync/stroke_diff/stroke_diff.rb +0 -9
- data/lib/util/ext/object.rb +0 -8
- data/lib/util/java_util.rb +0 -9
- data/lib/util/trigger_partition.rb +0 -136
- data/strokedb.rb +0 -75
|
@@ -1,4 +1,25 @@
|
|
|
1
1
|
module StrokeDB
|
|
2
|
+
|
|
3
|
+
class ::Array
|
|
4
|
+
def to_raw
|
|
5
|
+
map do |v|
|
|
6
|
+
v.respond_to?(:to_raw) ? v.to_raw : v
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class ::Hash
|
|
12
|
+
def to_raw
|
|
13
|
+
raw_hash = {}
|
|
14
|
+
map do |k,v|
|
|
15
|
+
_k = k.respond_to?(:to_raw) ? k.to_raw : k
|
|
16
|
+
_v = v.respond_to?(:to_raw) ? v.to_raw : v
|
|
17
|
+
raw_hash[_k] = _v
|
|
18
|
+
end
|
|
19
|
+
raw_hash
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
2
23
|
module JsonSerializationMethod
|
|
3
24
|
def serialize(x)
|
|
4
25
|
x.to_json
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
begin
|
|
2
|
+
module StrokeDB; end
|
|
3
|
+
class FastUUID
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'inline'
|
|
6
|
+
|
|
7
|
+
C_FLAGS = `uuid-config --cflags`.chomp
|
|
8
|
+
LD_FLAGS = `uuid-config --ldflags --libs`.chomp
|
|
9
|
+
|
|
10
|
+
inline(:C) do |builder|
|
|
11
|
+
builder.add_compile_flags C_FLAGS
|
|
12
|
+
builder.add_link_flags LD_FLAGS
|
|
13
|
+
builder.prefix %{
|
|
14
|
+
#include "dlfcn.h"
|
|
15
|
+
#include "uuid.h"
|
|
16
|
+
|
|
17
|
+
#define PROLOG(size) \\
|
|
18
|
+
uuid_t *uuid; \\
|
|
19
|
+
char str[size], *strptr = str; \\
|
|
20
|
+
size_t len = sizeof(str); \\
|
|
21
|
+
uuid_create(&uuid)
|
|
22
|
+
|
|
23
|
+
#define EPILOG(format, size) \\
|
|
24
|
+
uuid_export(uuid, format, &strptr, &len), \\
|
|
25
|
+
uuid_destroy(uuid), \\
|
|
26
|
+
rb_str_new(str,size)
|
|
27
|
+
|
|
28
|
+
#define CHARS EPILOG(UUID_FMT_STR, 36)
|
|
29
|
+
#define BINARY EPILOG(UUID_FMT_BIN, 16)
|
|
30
|
+
}
|
|
31
|
+
builder.c %{
|
|
32
|
+
VALUE random_uuid()
|
|
33
|
+
{
|
|
34
|
+
PROLOG(40);
|
|
35
|
+
uuid_make(uuid, UUID_MAKE_V4);
|
|
36
|
+
return CHARS;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
builder.c %{
|
|
40
|
+
VALUE random_uuid_raw()
|
|
41
|
+
{
|
|
42
|
+
PROLOG(20);
|
|
43
|
+
uuid_make(uuid, UUID_MAKE_V4);
|
|
44
|
+
return BINARY;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
builder.c %{
|
|
48
|
+
VALUE sha1_uuid(VALUE oid)
|
|
49
|
+
{
|
|
50
|
+
uuid_t *uuid_ns;
|
|
51
|
+
PROLOG(40);
|
|
52
|
+
|
|
53
|
+
uuid_create(&uuid_ns);
|
|
54
|
+
uuid_load(uuid_ns, "ns:OID");
|
|
55
|
+
uuid_make(uuid, UUID_MAKE_V5, uuid_ns, StringValuePtr(oid));
|
|
56
|
+
|
|
57
|
+
uuid_destroy(uuid_ns);
|
|
58
|
+
|
|
59
|
+
return CHARS;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
builder.c %{
|
|
64
|
+
VALUE uuid_to_raw(VALUE r_uuid)
|
|
65
|
+
{
|
|
66
|
+
PROLOG(20);
|
|
67
|
+
uuid_import(uuid, UUID_FMT_STR, StringValuePtr(r_uuid), 36);
|
|
68
|
+
return BINARY;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
builder.c %{
|
|
73
|
+
VALUE uuid_to_formatted(VALUE r_uuid)
|
|
74
|
+
{
|
|
75
|
+
PROLOG(40);
|
|
76
|
+
uuid_import(uuid, UUID_FMT_BIN, StringValuePtr(r_uuid), 36);
|
|
77
|
+
return CHARS;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
FAST_UUID = FastUUID.new
|
|
85
|
+
|
|
86
|
+
class ::String
|
|
87
|
+
# Convert to raw (16 bytes) string (self can be already raw or formatted).
|
|
88
|
+
def to_raw_uuid
|
|
89
|
+
if size == 16
|
|
90
|
+
self.freeze
|
|
91
|
+
else
|
|
92
|
+
FAST_UUID.uuid_to_raw(self)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
# Convert to formatted string (self can be raw or already formatted).
|
|
96
|
+
def to_formatted_uuid
|
|
97
|
+
if size == 16
|
|
98
|
+
FAST_UUID.uuid_to_formatted(self)
|
|
99
|
+
else
|
|
100
|
+
self.freeze
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
module StrokeDB::Util
|
|
106
|
+
|
|
107
|
+
def self.random_uuid
|
|
108
|
+
::FAST_UUID.random_uuid
|
|
109
|
+
end
|
|
110
|
+
def self.random_uuid_raw
|
|
111
|
+
::FAST_UUID.random_uuid_raw
|
|
112
|
+
end
|
|
113
|
+
def self.sha1_uuid(oid)
|
|
114
|
+
::FAST_UUID.sha1_uuid(oid)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
rescue NotImplementedError, CompilationError
|
|
122
|
+
if $!.is_a?(CompilationError)
|
|
123
|
+
puts "# Can't compile C code, make sure you have ossp-uuid installed"
|
|
124
|
+
else
|
|
125
|
+
puts "# Error: #{$!.message}"
|
|
126
|
+
end
|
|
127
|
+
puts "# Falling back to uuidtools gem"
|
|
128
|
+
require 'uuidtools'
|
|
129
|
+
module StrokeDB::Util
|
|
130
|
+
|
|
131
|
+
def self.random_uuid
|
|
132
|
+
::UUID.random_create.to_s
|
|
133
|
+
end
|
|
134
|
+
def self.random_uuid_raw
|
|
135
|
+
::UUID.random_create.raw
|
|
136
|
+
end
|
|
137
|
+
def self.sha1_uuid(oid)
|
|
138
|
+
::UUID.sha1_create(UUID_OID_NAMESPACE, oid).to_s
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
class ::String
|
|
142
|
+
# Convert to raw (16 bytes) string (self can be already raw or formatted).
|
|
143
|
+
def to_raw_uuid
|
|
144
|
+
size == 16 ? self.freeze : ::UUID.parse(self).raw
|
|
145
|
+
end
|
|
146
|
+
# Convert to formatted string (self can be raw or already formatted).
|
|
147
|
+
def to_formatted_uuid
|
|
148
|
+
size == 16 ? ::UUID.parse_raw(self).to_s : self.freeze
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
module StrokeDB::Util
|
|
156
|
+
RAW_UUID_SIZE = random_uuid_raw.size
|
|
157
|
+
FORMATTED_UUID_SIZE = random_uuid.size
|
|
158
|
+
end
|
|
159
|
+
|
|
File without changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
module StrokeDB
|
|
2
|
-
View = Meta.new
|
|
2
|
+
View = Meta.new do
|
|
3
3
|
attr_accessor :map_with_proc
|
|
4
4
|
attr_reader :reduce_with_proc
|
|
5
5
|
|
|
@@ -22,7 +22,7 @@ module StrokeDB
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
end
|
|
25
|
-
ViewCut = Meta.new
|
|
25
|
+
ViewCut = Meta.new do
|
|
26
26
|
|
|
27
27
|
on_new_document do |cut|
|
|
28
28
|
cut.instance_eval do
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'readbytes'
|
|
2
|
+
module StrokeDB
|
|
3
|
+
class ArchiveVolume
|
|
4
|
+
attr_reader :file_path, :tail
|
|
5
|
+
|
|
6
|
+
if defined?(::Spec)
|
|
7
|
+
DEFAULT_SIZE = 512*1024
|
|
8
|
+
else
|
|
9
|
+
DEFAULT_SIZE = 64*1024*1024
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
DEFAULT_PATH = "."
|
|
13
|
+
|
|
14
|
+
# Open a volume in a directory +:path+, with UUID +:uuid+
|
|
15
|
+
# and a specified +:size+. If the file does not exist, it is created
|
|
16
|
+
# and filled with zero bytes up to the specified size.
|
|
17
|
+
# Otherwise, it is just opened and ready for reads and writes.
|
|
18
|
+
#
|
|
19
|
+
# Defaults:
|
|
20
|
+
# :path => "."
|
|
21
|
+
# :size => 64 Mb
|
|
22
|
+
#
|
|
23
|
+
# Example:
|
|
24
|
+
# DataVolume.new(:uuid => uuid, :path => "/var/dir", :size => 1024)
|
|
25
|
+
#
|
|
26
|
+
def initialize(options = {})
|
|
27
|
+
@options = options.stringify_keys.reverse_merge('size' => DEFAULT_SIZE, 'path' => DEFAULT_PATH)
|
|
28
|
+
initialize_file
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Read a record sitting in a +position+ in the volume file.
|
|
32
|
+
# Record length is stored in a first 4 bytes before the record.
|
|
33
|
+
#
|
|
34
|
+
def read(position)
|
|
35
|
+
@file.seek(position)
|
|
36
|
+
size = @file.readbytes(4).unpack('N').first
|
|
37
|
+
@file.readbytes(size)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Write some data to the end of the file.
|
|
41
|
+
# Returns record position.
|
|
42
|
+
#
|
|
43
|
+
def insert(data)
|
|
44
|
+
raise VolumeCapacityExceeded if (new_tail = @tail + 4 + data.size) > size
|
|
45
|
+
@file.seek(@tail)
|
|
46
|
+
@file.write([data.size].pack('N') + data)
|
|
47
|
+
t = @tail
|
|
48
|
+
@tail = new_tail
|
|
49
|
+
write_tail(@file, @tail)
|
|
50
|
+
t
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Close the volume file. You cannot read/insert after that operation.
|
|
54
|
+
# In such case, VolumeClosedException is raised.
|
|
55
|
+
# Call DataVolume.new to open volume again.
|
|
56
|
+
#
|
|
57
|
+
def close!
|
|
58
|
+
safe_close
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Close and delete the volume file. You cannot read/insert after that
|
|
62
|
+
# operation. In such case, VolumeClosedException is raised.
|
|
63
|
+
#
|
|
64
|
+
def delete!
|
|
65
|
+
safe_close
|
|
66
|
+
File.delete(@file_path)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def path
|
|
70
|
+
@options['path']
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def size
|
|
74
|
+
@options['size']
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def uuid
|
|
78
|
+
case @options['uuid']
|
|
79
|
+
when /^#{UUID_RE}$/
|
|
80
|
+
@options['uuid']
|
|
81
|
+
when nil
|
|
82
|
+
@options['uuid'] = Util.random_uuid
|
|
83
|
+
else
|
|
84
|
+
@options['uuid'] = @options['uuid'].to_formatted_uuid
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def raw_uuid
|
|
89
|
+
@raw_uuid ||= uuid.to_raw_uuid
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# VolumeClosedException is thrown when you call +read+ or +insert+
|
|
93
|
+
# method on a closed or deleted volume.
|
|
94
|
+
#
|
|
95
|
+
class VolumeClosedException < Exception; end
|
|
96
|
+
|
|
97
|
+
# VolumeCapacityExceeded is thrown when you are trying to +insert+ data
|
|
98
|
+
# that is larger than available capacity.
|
|
99
|
+
class VolumeCapacityExceeded < Exception; end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def initialize_file
|
|
104
|
+
@file_path = File.join(path, hierarchify(uuid) + ".av")
|
|
105
|
+
unless File.exist?(@file_path)
|
|
106
|
+
create_file(@file_path, size)
|
|
107
|
+
else
|
|
108
|
+
@file = File.open(@file_path, File::RDWR)
|
|
109
|
+
end
|
|
110
|
+
@tail = read_tail(@file)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Create file skeleton filled with zeros with a prefix
|
|
114
|
+
# containing current file tail.
|
|
115
|
+
#
|
|
116
|
+
def create_file(path, size)
|
|
117
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
118
|
+
@file = File.open(path, File::CREAT | File::EXCL | File::RDWR)
|
|
119
|
+
@file.truncate(size)
|
|
120
|
+
write_tail(@file, 4) # 4 is a size of long type.
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Close the file if it is opened and remove
|
|
124
|
+
# +read+ and +write+ methods from the instance.
|
|
125
|
+
#
|
|
126
|
+
def safe_close
|
|
127
|
+
@file.close if @file
|
|
128
|
+
@file = nil
|
|
129
|
+
class <<self
|
|
130
|
+
alias :read :raise_volume_closed
|
|
131
|
+
alias :insert :raise_volume_closed
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# +read+ and +write+ methods are aliased to this
|
|
136
|
+
# when file is closed or deleted.
|
|
137
|
+
#
|
|
138
|
+
def raise_volume_closed(*args)
|
|
139
|
+
raise VolumeClosedException, "Throw this object away and instantiate another one."
|
|
140
|
+
end
|
|
141
|
+
public :raise_volume_closed
|
|
142
|
+
|
|
143
|
+
# Transform filename "aabbccdd" into "aa/bb/aabbccdd"
|
|
144
|
+
# for faster access to a bunch of datavolumes.
|
|
145
|
+
#
|
|
146
|
+
def hierarchify(filename)
|
|
147
|
+
File.join(filename[0,2], filename[2,2], filename)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Read current file end position ("tail") from the file header.
|
|
151
|
+
#
|
|
152
|
+
def read_tail(f)
|
|
153
|
+
f.seek(0)
|
|
154
|
+
f.readbytes(4).unpack('N').first
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Update file's end position.
|
|
158
|
+
#
|
|
159
|
+
def write_tail(f, pos)
|
|
160
|
+
f.seek(0)
|
|
161
|
+
f.write([pos].pack('N'))
|
|
162
|
+
pos
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
require 'readbytes'
|
|
2
|
+
module StrokeDB
|
|
3
|
+
|
|
4
|
+
# TODO: inherit from the common AbstractVolume
|
|
5
|
+
class BlockVolume
|
|
6
|
+
attr_reader :file_path, :blocks_count
|
|
7
|
+
|
|
8
|
+
HEADER_LENGTH = 8 # block_size, blocks_count
|
|
9
|
+
DEFAULT_BLOCKS_COUNT = 1024
|
|
10
|
+
DEFAULT_PATH = "."
|
|
11
|
+
|
|
12
|
+
# Open a volume in a directory +:path+, with UUID +:uuid+
|
|
13
|
+
# and a specified +:block_size+. If the file does not exist, it is created
|
|
14
|
+
# and filled with zero bytes up to the specified size.
|
|
15
|
+
# Otherwise, it is just opened and ready for reads and writes.
|
|
16
|
+
# File contains +block_count+ blocks of :block_size: bytes size.
|
|
17
|
+
# When insertion is done to a new position, file is autoextended.
|
|
18
|
+
#
|
|
19
|
+
# Required params: +:block_size+ and +:uuid+
|
|
20
|
+
# Default +:path+ is ".", +:blocks_count+ is 1024
|
|
21
|
+
#
|
|
22
|
+
# Example:
|
|
23
|
+
# DataVolume.new(:uuid => uuid, :path => "/var/dir", :block_size => 1024)
|
|
24
|
+
#
|
|
25
|
+
def initialize(options = {})
|
|
26
|
+
@options = options.stringify_keys.reverse_merge(
|
|
27
|
+
'path' => DEFAULT_PATH,
|
|
28
|
+
'blocks_count' => DEFAULT_BLOCKS_COUNT)
|
|
29
|
+
initialize_file
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Read a record sitting in a +position+ in the volume file.
|
|
33
|
+
# Record length is stored in a first 4 bytes before the record.
|
|
34
|
+
#
|
|
35
|
+
def read(index)
|
|
36
|
+
csize = @block_size
|
|
37
|
+
@file.seek(HEADER_LENGTH + index * csize)
|
|
38
|
+
@file.readbytes(csize)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Write some data to the end of the file.
|
|
42
|
+
# Returns record position.
|
|
43
|
+
#
|
|
44
|
+
def insert(index, data)
|
|
45
|
+
extend_volume! if index >= @blocks_count
|
|
46
|
+
csize = @block_size
|
|
47
|
+
@file.seek(HEADER_LENGTH + index * csize)
|
|
48
|
+
@file.write(data)
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Close the volume file. You cannot read/insert after that operation.
|
|
53
|
+
# In such case, VolumeClosedException is raised.
|
|
54
|
+
# Call DataVolume.new to open volume again.
|
|
55
|
+
#
|
|
56
|
+
def close!
|
|
57
|
+
safe_close
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Close and delete the volume file. You cannot read/insert after that
|
|
61
|
+
# operation. In such case, VolumeClosedException is raised.
|
|
62
|
+
#
|
|
63
|
+
def delete!
|
|
64
|
+
safe_close
|
|
65
|
+
File.delete(@file_path)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def path
|
|
69
|
+
@options['path']
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def block_size
|
|
73
|
+
@options['block_size']
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def blocks_count
|
|
77
|
+
@options['blocks_count']
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def uuid
|
|
81
|
+
case @options['uuid']
|
|
82
|
+
when /^#{UUID_RE}$/
|
|
83
|
+
@options['uuid']
|
|
84
|
+
when nil
|
|
85
|
+
@options['uuid'] = Util.random_uuid
|
|
86
|
+
else
|
|
87
|
+
@options['uuid'] = @options['uuid'].to_formatted_uuid
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# VolumeClosedException is thrown when you call +read+ or +insert+
|
|
92
|
+
# method on a closed or deleted volume.
|
|
93
|
+
#
|
|
94
|
+
class VolumeClosedException < Exception; end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def initialize_file
|
|
99
|
+
@file_path = File.join(path, hierarchify(uuid) + ".blocks")
|
|
100
|
+
create_file(@file_path, block_size, blocks_count) unless File.exist?(@file_path)
|
|
101
|
+
@file = File.open(@file_path, File::RDWR)
|
|
102
|
+
@block_size, @blocks_count = read_header(@file)
|
|
103
|
+
if @block_size != block_size && block_size != nil
|
|
104
|
+
raise "Block size collision! Declared #{block_size} bytes, but actually file is formatted by #{@block_size}-byte blocks."
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Extends volume to a double size.
|
|
109
|
+
#
|
|
110
|
+
def extend_volume!
|
|
111
|
+
@file.close
|
|
112
|
+
@blocks_count *= 2
|
|
113
|
+
create_file(@file_path, @block_size, @blocks_count)
|
|
114
|
+
@file = File.open(@file_path, File::RDWR)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Create file skeleton filled with zeros with a prefix
|
|
118
|
+
# containing current file tail.
|
|
119
|
+
#
|
|
120
|
+
def create_file(path, block_size, blocks)
|
|
121
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
122
|
+
File.open(path, File::CREAT | File::WRONLY) do |f|
|
|
123
|
+
f.truncate(HEADER_LENGTH + block_size*blocks)
|
|
124
|
+
write_header(f, block_size, blocks)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Close the file if it is opened and remove
|
|
129
|
+
# +read+ and +write+ methods from the instance.
|
|
130
|
+
#
|
|
131
|
+
def safe_close
|
|
132
|
+
@file.close if @file
|
|
133
|
+
@file = nil
|
|
134
|
+
class <<self
|
|
135
|
+
alias :read :raise_volume_closed
|
|
136
|
+
alias :insert :raise_volume_closed
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# +read+ and +insert+ methods are aliased to this
|
|
141
|
+
# when file is closed or deleted.
|
|
142
|
+
#
|
|
143
|
+
def raise_volume_closed(*args)
|
|
144
|
+
raise VolumeClosedException, "Throw this object away and instantiate another one."
|
|
145
|
+
end
|
|
146
|
+
public :raise_volume_closed
|
|
147
|
+
|
|
148
|
+
# Transform filename "aabbccdd" into "aa/bb/aabbccdd"
|
|
149
|
+
# for faster access to a bunch of datavolumes.
|
|
150
|
+
#
|
|
151
|
+
def hierarchify(filename)
|
|
152
|
+
File.join(filename[0,2], filename[2,2], filename)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Read current file block size and blocks count.
|
|
156
|
+
#
|
|
157
|
+
def read_header(f)
|
|
158
|
+
f.seek(0)
|
|
159
|
+
f.readbytes(8).unpack('N2')
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Update file's end position.
|
|
163
|
+
#
|
|
164
|
+
def write_header(f, block_size, blocks)
|
|
165
|
+
f.seek(0)
|
|
166
|
+
f.write([block_size, blocks].pack('N2'))
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|