@@ -76,7 +76,8 @@ struct MemoryMappedFile {
7676 ~MemoryMappedFile () { unmap (); }
7777};
7878
79- class BBFReader {
79+ class BBFReader
80+ {
8081private:
8182 const char * data_ptr = nullptr ;
8283 const BBFSection* sections_ = nullptr ;
@@ -92,7 +93,8 @@ class BBFReader {
9293 MemoryMappedFile mmap;
9394 bool isValid = false ;
9495
95- BBFReader (const std::string& path) {
96+ BBFReader (const std::string& path)
97+ {
9698 if (!mmap.map (path)) return ;
9799 data_ptr = static_cast <const char *>(mmap.data );
98100
@@ -121,24 +123,31 @@ class BBFReader {
121123 isValid = true ;
122124 }
123125
124- std::string_view getStringView (uint32_t offset) const {
126+ // Get string view
127+ std::string_view getStringView (uint32_t offset) const
128+ {
125129 if (offset >= stringPoolSize_) return {};
126130 return std::string_view (stringPool_ + offset);
127131 }
128132
129- struct PySection {
133+ struct PySection
134+ {
130135 std::string title;
131136 uint32_t startPage;
132137 uint32_t parent;
133138 };
134139
135- std::vector<PySection> getSections () const {
140+ // Get sections
141+ std::vector<PySection> getSections () const
142+ {
136143 std::vector<PySection> result;
137144 if (!isValid) return result;
138145
139146 result.reserve (footer.sectionCount );
140- for (uint32_t i = 0 ; i < footer.sectionCount ; i++) {
141- result.push_back ({
147+ for (uint32_t i = 0 ; i < footer.sectionCount ; i++)
148+ {
149+ result.push_back (
150+ {
142151 std::string (getStringView (sections_[i].sectionTitleOffset )),
143152 sections_[i].sectionStartIndex ,
144153 sections_[i].parentSectionIndex
@@ -147,12 +156,15 @@ class BBFReader {
147156 return result;
148157 }
149158
150- std::vector<std::pair<std::string, std::string>> getMetadata () const {
159+ // Get metadata
160+ std::vector<std::pair<std::string, std::string>> getMetadata () const
161+ {
151162 std::vector<std::pair<std::string, std::string>> result;
152163 if (!isValid) return result;
153164
154165 result.reserve (footer.keyCount );
155- for (uint32_t i = 0 ; i < footer.keyCount ; i++) {
166+ for (uint32_t i = 0 ; i < footer.keyCount ; i++)
167+ {
156168 result.emplace_back (
157169 getStringView (meta_[i].keyOffset ),
158170 getStringView (meta_[i].valOffset )
@@ -161,7 +173,9 @@ class BBFReader {
161173 return result;
162174 }
163175
164- std::pair<const char *, size_t > getPageRaw (uint32_t pageIndex) const {
176+ // Get page raw
177+ std::pair<const char *, size_t > getPageRaw (uint32_t pageIndex) const
178+ {
165179 if (!isValid || pageIndex >= footer.pageCount ) return {nullptr , 0 };
166180
167181 const auto & asset = assets_[pages_[pageIndex].assetIndex ];
@@ -171,7 +185,36 @@ class BBFReader {
171185 return { data_ptr + asset.offset , static_cast <size_t >(asset.length ) };
172186 }
173187
174- std::map<std::string, uint64_t > getPageInfo (uint32_t pageIndex) const {
188+ // Get footer info
189+ std::map<std::string, uint64_t > getFooterInfo () const
190+ {
191+ // If invalid, return empty set.
192+ if (!isValid) return {};
193+
194+ // Otherwise return all this juicy information!
195+ return
196+ {
197+ {" stringPoolOffset" , footer.stringPoolOffset },
198+ {" assetTableOffset" , footer.assetTableOffset },
199+ {" assetCount" , static_cast <uint64_t >(footer.assetCount )},
200+
201+ {" pageTableOffset" , footer.pageTableOffset },
202+ {" pageCount" , static_cast <uint64_t >(footer.pageCount )},
203+
204+ {" sectionTableOffset" , footer.sectionTableOffset },
205+ {" sectionCount" , footer.sectionCount },
206+
207+ {" metaTableOffset" , footer.metaTableOffset },
208+ {" keyCount" , static_cast <uint64_t >(footer.keyCount )},
209+
210+ {" extraOffset" , footer.extraOffset },
211+ {" indexHash" , footer.indexHash }
212+ };
213+ }
214+
215+ // Get page info
216+ std::map<std::string, uint64_t > getPageInfo (uint32_t pageIndex) const
217+ {
175218 if (!isValid || pageIndex >= footer.pageCount ) return {};
176219
177220 const auto & asset = assets_[pages_[pageIndex].assetIndex ];
@@ -180,12 +223,29 @@ class BBFReader {
180223 {" offset" , asset.offset },
181224 {" hash" , asset.xxh3Hash },
182225 {" type" , asset.type },
226+ {" flags" , asset.flags }, // Add flags
183227 {" decodedLength" , asset.decodedLength } // ADDED: v1.1 Spec
184228 };
185229 }
186230
231+ // verify a specific page
232+ std::map<uint64_t , bool > verifyPage (uint32_t pageIndex)
233+ {
234+ if (!isValid || pageIndex >= footer.pageCount ) return {};
235+ // Get page
236+ const auto & asset = assets_[pages_[pageIndex].assetIndex ];
237+ // Check mmap length
238+ if (asset.offset + asset.length > mmap.size ) return {};
239+ // hash
240+ uint64_t xxhHash = XXH3_64bits ((const uint8_t *)data_ptr + asset.offset , asset.length );
241+
242+ bool match = (xxhHash == asset.xxh3Hash );
243+ return {{xxhHash, match}};
244+ }
245+
187246 // Returns -1 for Success, -2 for Directory Error, or >=0 for Asset Index Error
188- int64_t verify () const {
247+ int64_t verify () const
248+ {
189249 if (!isValid) return -2 ;
190250
191251 // 1. Directory Hash Check
@@ -200,13 +260,16 @@ class BBFReader {
200260 size_t max_size = mmap.size ;
201261
202262 // Lambda returns -1 if OK, or the index if Bad
203- auto verifyRange = [local_assets, local_data, max_size](size_t start, size_t end) -> int64_t {
204- for (size_t i = start; i < end; ++i) {
263+ auto verifyRange = [local_assets, local_data, max_size](size_t start, size_t end) -> int64_t
264+ {
265+ for (size_t i = start; i < end; ++i)
266+ {
205267 const auto & a = local_assets[i];
206268 // Bounds check before hash
207269 if (a.offset + a.length > max_size) return (int64_t )i;
208270
209- if (XXH3_64bits ((const uint8_t *)local_data + a.offset , a.length ) != a.xxh3Hash ) {
271+ if (XXH3_64bits ((const uint8_t *)local_data + a.offset , a.length ) != a.xxh3Hash )
272+ {
210273 return (int64_t )i; // Return the corrupted index
211274 }
212275 }
@@ -216,22 +279,25 @@ class BBFReader {
216279 size_t numThreads = std::thread::hardware_concurrency ();
217280 if (numThreads == 0 ) numThreads = 1 ;
218281
219- if (count < 128 || numThreads == 1 ) {
282+ if (count < 128 || numThreads == 1 )
283+ {
220284 return verifyRange (0 , count);
221285 }
222286
223287 size_t chunkSize = count / numThreads;
224288 std::vector<std::future<int64_t >> futures; // Changed from bool to int64_t
225289 futures.reserve (numThreads);
226290
227- for (size_t i = 0 ; i < numThreads; ++i) {
291+ for (size_t i = 0 ; i < numThreads; ++i)
292+ {
228293 size_t start = i * chunkSize;
229294 size_t end = (i == numThreads - 1 ) ? count : start + chunkSize;
230295 futures.push_back (std::async (std::launch::async, verifyRange, start, end));
231296 }
232297
233298 // Check results
234- for (auto & f : futures) {
299+ for (auto & f : futures)
300+ {
235301 int64_t result = f.get ();
236302 if (result != -1 ) return result; // Bubble up the error index
237303 }
0 commit comments