9
9
10
10
using namespace rlib ;
11
11
12
- static constexpr auto rcache_file_flags (RCache::Options const & options ) -> IO::Flags {
13
- return (options. readonly ? IO::READ : IO::WRITE) | IO::NO_INTERUPT | IO::NO_OVERGROW;
12
+ static constexpr auto rcache_file_flags (bool readonly ) -> IO::Flags {
13
+ return (readonly ? IO::READ : IO::WRITE) | IO::NO_INTERUPT | IO::NO_OVERGROW;
14
14
}
15
15
16
- RCache::RCache (Options const & options) : file_(options.path, rcache_file_flags(options)), options_(options) {
17
- auto file_size = file_.size ();
18
- if (file_size == 0 && !options.readonly ) {
19
- flush ();
20
- return ;
16
+ static auto rcache_file_path (fs::path base, std::size_t index) -> fs::path {
17
+ if (!index ) return base;
18
+ return std::move (base.replace_extension (fmt::format (" .{:05d}.bundle" , index )));
19
+ }
20
+
21
+ RCache::RCache (Options const & options) : options_(options) {
22
+ if (!options_.readonly ) {
23
+ options_.flush_size = std::max (32 * MiB, options_.flush_size );
24
+ options_.max_size = std::max (options_.flush_size * 2 , options_.max_size ) - options_.flush_size ;
25
+ }
26
+ for (fs::path path = options_.path ;;) {
27
+ auto const index = files_.size ();
28
+ auto next_path = rcache_file_path (options_.path , index + 1 );
29
+ auto const next_exists = fs::exists (next_path);
30
+ auto const flags = rcache_file_flags (options_.readonly || next_exists);
31
+
32
+ auto file = std::make_unique<IO::File>(path, flags);
33
+ auto const is_empty = file->size () == 0 ;
34
+ auto bundle = !is_empty ? RBUN::read (*file) : RBUN{};
35
+ files_.push_back (std::move (file));
36
+ for (auto & chunk : bundle.lookup ) {
37
+ chunk.second .bundleId = (BundleID)index ;
38
+ }
39
+ lookup_.merge (std::move (bundle.lookup ));
40
+
41
+ if (flags & IO::WRITE) {
42
+ writer_ = {
43
+ .toc_offset = bundle.toc_offset ,
44
+ .end_offset = bundle.toc_offset + sizeof (RBUN::Footer),
45
+ .chunks = std::move (bundle.chunks ),
46
+ };
47
+ writer_.end_offset += sizeof (RChunk) * writer_.chunks .size ();
48
+ writer_.buffer .reserve (options_.flush_size * 2 );
49
+ can_write_ = true ;
50
+ if (is_empty) {
51
+ this ->flush ();
52
+ } else {
53
+ this ->check_space (options_.flush_size );
54
+ }
55
+ }
56
+
57
+ if (!next_exists) {
58
+ break ;
59
+ }
60
+
61
+ path = std::move (next_path);
21
62
}
22
- bundle_ = RBUN::read (file_);
23
63
}
24
64
25
65
RCache::~RCache () { this ->flush (); }
26
66
27
67
auto RCache::add (RChunk const & chunk, std::span<char const > data) -> bool {
28
68
rlib_assert (chunk.compressed_size == data.size ());
29
- if (!can_write () || bundle_. lookup .contains (chunk.chunkId )) {
69
+ if (!can_write () || lookup_ .contains (chunk.chunkId )) {
30
70
return false ;
31
71
}
32
72
if (chunk.chunkId == ChunkID::None) {
33
73
return false ;
34
74
}
35
- bundle_.chunks .push_back (chunk);
36
- bundle_.lookup [chunk.chunkId ] = {chunk, BundleID::None, buffer_.size () + bundle_.toc_offset };
37
- buffer_.insert (buffer_.end (), data.begin (), data.end ());
38
- if (buffer_.size () > options_.flush_size ) {
75
+
76
+ // check if we hit chunk limit
77
+ auto const extra_data = sizeof (RChunk) + data.size ();
78
+ this ->check_space (extra_data);
79
+
80
+ writer_.chunks .push_back (chunk);
81
+ lookup_[chunk.chunkId ] = {chunk, BundleID::None, writer_.buffer .size () + writer_.toc_offset };
82
+ writer_.buffer .insert (writer_.buffer .end (), data.begin (), data.end ());
83
+ if (writer_.buffer .size () > options_.flush_size ) {
39
84
this ->flush ();
40
85
}
86
+ writer_.end_offset += extra_data;
87
+
41
88
return true ;
42
89
}
43
90
@@ -58,11 +105,11 @@ auto RCache::add_uncompressed(std::span<char const> src, int level) -> RChunk::S
58
105
return chunk;
59
106
}
60
107
61
- auto RCache::contains (ChunkID chunkId) const noexcept -> bool { return bundle_. lookup .contains (chunkId); }
108
+ auto RCache::contains (ChunkID chunkId) const noexcept -> bool { return lookup_ .contains (chunkId); }
62
109
63
110
auto RCache::find (ChunkID chunkId) const noexcept -> RChunk::Src {
64
- auto i = bundle_. lookup .find (chunkId);
65
- if (i == bundle_. lookup .end ()) {
111
+ auto i = lookup_ .find (chunkId);
112
+ if (i == lookup_ .end ()) {
66
113
return {};
67
114
}
68
115
return i->second ;
@@ -82,6 +129,7 @@ auto RCache::uncache(std::vector<RChunk::Dst> chunks, RChunk::Dst::data_cb on_da
82
129
rlib_assert (c.uncompressed_size == chunk.uncompressed_size );
83
130
chunk.compressed_offset = c.compressed_offset ;
84
131
chunk.compressed_size = c.compressed_size ;
132
+ chunk.bundleId = c.bundleId ;
85
133
found.push_back (chunk);
86
134
return true ;
87
135
});
@@ -95,11 +143,12 @@ auto RCache::uncache(std::vector<RChunk::Dst> chunks, RChunk::Dst::data_cb on_da
95
143
on_data (chunk, dst);
96
144
continue ;
97
145
}
98
- auto src = std::span (buffer_);
99
- if (chunk.compressed_offset > bundle_.toc_offset ) {
100
- src = src.subspan (chunk.compressed_offset - bundle_.toc_offset , chunk.compressed_size );
146
+ auto src = std::span<char const >{};
147
+ auto const & file = files_.at ((std::size_t )chunk.bundleId );
148
+ if (can_write () && &file == &files_.back () && chunk.compressed_offset > writer_.toc_offset ) {
149
+ src = src.subspan (chunk.compressed_offset - writer_.toc_offset , chunk.compressed_size );
101
150
} else {
102
- src = file_. copy (chunk.compressed_offset , chunk.compressed_size );
151
+ src = file-> copy (chunk.compressed_offset , chunk.compressed_size );
103
152
}
104
153
dst = zstd_decompress (src, chunk.uncompressed_size );
105
154
on_data (chunk, dst);
@@ -108,23 +157,49 @@ auto RCache::uncache(std::vector<RChunk::Dst> chunks, RChunk::Dst::data_cb on_da
108
157
return std::move (chunks);
109
158
}
110
159
160
+ auto RCache::check_space (std::size_t extra) -> bool {
161
+ // ensure we can allways at least write one file
162
+ if (writer_.end_offset <= sizeof (RBUN::Footer)) {
163
+ return false ;
164
+ }
165
+ // still have space
166
+ if (writer_.end_offset + extra < options_.max_size ) {
167
+ return false ;
168
+ }
169
+ this ->flush (); // flush anything that we have atm
170
+ auto const index = files_.size ();
171
+ auto const path = rcache_file_path (options_.path , index );
172
+ auto const flags = rcache_file_flags (false );
173
+ auto file = std::make_unique<IO::File>(path, flags);
174
+ file->resize (0 , 0 );
175
+ files_.push_back (std::move (file));
176
+ writer_.toc_offset = 0 ;
177
+ writer_.end_offset = sizeof (RBUN::Footer);
178
+ writer_.chunks .clear ();
179
+ writer_.buffer .clear ();
180
+ this ->flush ();
181
+ return true ;
182
+ }
183
+
111
184
auto RCache::flush () -> bool {
112
185
// Dont reflush when there is nothing to flush.
113
- if (!can_write () || (buffer_. empty () && bundle_ .toc_offset != 0 )) {
186
+ if (!can_write () || (writer_. buffer . empty () && writer_ .toc_offset != 0 )) {
114
187
return false ;
115
188
}
116
- auto toc_size = sizeof (RChunk) * bundle_ .chunks .size ();
189
+ auto toc_size = sizeof (RChunk) * writer_ .chunks .size ();
117
190
RBUN::Footer footer = {
118
- .checksum = std::bit_cast<std::array<char , 8 >>(XXH64 ((char const *)bundle_ .chunks .data (), toc_size, 0 )),
119
- .entry_count = (std::uint32_t )bundle_ .chunks .size (),
191
+ .checksum = std::bit_cast<std::array<char , 8 >>(XXH64 ((char const *)writer_ .chunks .data (), toc_size, 0 )),
192
+ .entry_count = (std::uint32_t )writer_ .chunks .size (),
120
193
.version = RBUN::Footer::VERSION,
121
194
.magic = {' R' , ' B' , ' U' , ' N' },
122
195
};
123
- auto new_toc_offset = bundle_.toc_offset + buffer_.size ();
124
- buffer_.insert (buffer_.end (), (char const *)bundle_.chunks .data (), (char const *)bundle_.chunks .data () + toc_size);
125
- buffer_.insert (buffer_.end (), (char const *)&footer, (char const *)&footer + sizeof (footer));
126
- rlib_assert (file_.write (bundle_.toc_offset , buffer_));
127
- buffer_.clear ();
128
- bundle_.toc_offset = new_toc_offset;
196
+ auto new_toc_offset = writer_.toc_offset + writer_.buffer .size ();
197
+ writer_.buffer .insert (writer_.buffer .end (),
198
+ (char const *)writer_.chunks .data (),
199
+ (char const *)writer_.chunks .data () + toc_size);
200
+ writer_.buffer .insert (writer_.buffer .end (), (char const *)&footer, (char const *)&footer + sizeof (footer));
201
+ rlib_assert (files_.back ()->write (writer_.toc_offset , writer_.buffer ));
202
+ writer_.buffer .clear ();
203
+ writer_.toc_offset = new_toc_offset;
129
204
return true ;
130
205
}
0 commit comments