11// SPDX-License-Identifier: Apache-2.0
22// SPDX-FileCopyrightText: Copyright the Vortex contributors
33
4+ use std:: collections:: VecDeque ;
45use std:: fs:: File ;
56use std:: io:: { Read , Seek , SeekFrom } ;
67use std:: path:: PathBuf ;
8+ use std:: sync:: Arc ;
79
810use flatbuffers:: root;
11+ use itertools:: Itertools ;
912use vortex:: buffer:: { Alignment , ByteBuffer } ;
10- use vortex:: error:: { VortexResult , vortex_bail, vortex_err} ;
11- use vortex:: file:: { EOF_SIZE , MAGIC_BYTES , MAX_FOOTER_SIZE , VERSION } ;
13+ use vortex:: error:: { VortexExpect , VortexResult , vortex_bail, vortex_err} ;
14+ use vortex:: file:: { EOF_SIZE , Footer , MAGIC_BYTES , MAX_FOOTER_SIZE , VERSION , VortexOpenOptions } ;
1215use vortex:: flatbuffers:: footer as fb;
16+ use vortex:: layout:: LayoutRef ;
1317
1418#[ derive( Debug , clap:: Parser ) ]
1519pub struct InspectArgs {
@@ -75,19 +79,18 @@ pub fn exec_inspect(args: InspectArgs) -> anyhow::Result<()> {
7579 return Ok ( ( ) ) ;
7680 }
7781
78- let postscript = match inspector. read_postscript ( eof. postscript_size ) {
82+ match inspector. read_postscript ( eof. postscript_size ) {
7983 Ok ( ps) => {
8084 ps. display ( ) ;
81- ps
8285 }
8386 Err ( e) => {
8487 eprintln ! ( "\n Error reading postscript: {}" , e) ;
8588 return Ok ( ( ) ) ;
8689 }
8790 } ;
8891
89- match inspector. read_footer_segments ( & postscript ) {
90- Ok ( footer) => footer. display ( ) ,
92+ match inspector. read_footer ( ) {
93+ Ok ( footer) => FooterSegments ( footer) . display ( ) ,
9194 Err ( e) => {
9295 eprintln ! ( "\n Error reading footer segments: {}" , e) ;
9396 }
@@ -99,6 +102,7 @@ pub fn exec_inspect(args: InspectArgs) -> anyhow::Result<()> {
99102}
100103
101104struct VortexInspector {
105+ path : PathBuf ,
102106 file : File ,
103107 file_size : u64 ,
104108}
@@ -112,7 +116,11 @@ impl VortexInspector {
112116 . seek ( SeekFrom :: End ( 0 ) )
113117 . map_err ( |e| vortex_err ! ( "Failed to get file size: {}" , e) ) ?;
114118
115- Ok ( Self { file, file_size } )
119+ Ok ( Self {
120+ path,
121+ file,
122+ file_size,
123+ } )
116124 }
117125
118126 fn read_eof ( & mut self ) -> VortexResult < EofInfo > {
@@ -197,45 +205,11 @@ impl VortexInspector {
197205 } )
198206 }
199207
200- fn read_footer_segments (
201- & mut self ,
202- postscript : & PostscriptInfo ,
203- ) -> VortexResult < FooterSegments > {
204- // Read footer segment
205-
206- let mut footer_bytes = vec ! [ 0u8 ; postscript. footer. length as usize ] ;
207- self . file
208- . seek ( SeekFrom :: Start ( postscript. footer . offset ) )
209- . map_err ( |e| vortex_err ! ( "Failed to seek to footer: {}" , e) ) ?;
210- self . file
211- . read_exact ( & mut footer_bytes)
212- . map_err ( |e| vortex_err ! ( "Failed to read footer: {}" , e) ) ?;
213-
214- let footer_buffer = ByteBuffer :: from ( footer_bytes) ;
215- let fb_footer = root :: < fb:: Footer > ( & footer_buffer)
216- . map_err ( |e| vortex_err ! ( "Failed to parse footer flatbuffer: {}" , e) ) ?;
217-
218- let segment_count = fb_footer
219- . segment_specs ( )
220- . map ( |segs| segs. len ( ) )
221- . unwrap_or ( 0 ) ;
222-
223- let mut segments = Vec :: new ( ) ;
224- if let Some ( fb_segments) = fb_footer. segment_specs ( ) {
225- for seg in fb_segments {
226- segments. push ( SegmentInfo {
227- offset : seg. offset ( ) ,
228- length : seg. length ( ) ,
229- alignment : Alignment :: from_exponent ( seg. alignment_exponent ( ) ) ,
230- } ) ;
231- }
232- }
233-
234- Ok ( FooterSegments {
235- segment_count,
236- total_data_size : segments. iter ( ) . map ( |s| s. length as u64 ) . sum ( ) ,
237- segments,
238- } )
208+ fn read_footer ( & mut self ) -> VortexResult < Footer > {
209+ Ok ( VortexOpenOptions :: file ( )
210+ . open_blocking ( & self . path ) ?
211+ . footer ( )
212+ . clone ( ) )
239213 }
240214}
241215
@@ -263,11 +237,7 @@ struct PostscriptInfo {
263237}
264238
265239#[ derive( Debug ) ]
266- struct FooterSegments {
267- segment_count : usize ,
268- segments : Vec < SegmentInfo > ,
269- total_data_size : u64 ,
270- }
240+ struct FooterSegments ( Footer ) ;
271241
272242impl EofInfo {
273243 fn display ( & self ) {
@@ -319,17 +289,107 @@ impl PostscriptInfo {
319289impl FooterSegments {
320290 fn display ( & self ) {
321291 println ! ( "\n === Footer Segments ===" ) ;
322- println ! ( "Total segments: {}" , self . segment_count) ;
323- println ! ( "Total data size: {} bytes" , self . total_data_size) ;
324-
325- if !self . segments . is_empty ( ) {
326- println ! ( "\n Segment details:" ) ;
327- for ( i, segment) in self . segments . iter ( ) . enumerate ( ) {
328- println ! (
329- " [{}] offset={}, length={}, alignment={}" ,
330- i, segment. offset, segment. length, segment. alignment
331- ) ;
292+ println ! ( "Total segments: {}" , self . 0 . segment_map( ) . len( ) ) ;
293+ let total_size = self
294+ . 0
295+ . segment_map ( )
296+ . iter ( )
297+ . map ( |s| s. length as u64 )
298+ . sum :: < u64 > ( ) ;
299+ println ! ( "Total data size: {} bytes" , total_size) ;
300+
301+ println ! ( "\n Segment details:\n " ) ;
302+
303+ let segment_map = self . 0 . segment_map ( ) . clone ( ) ;
304+ if segment_map. is_empty ( ) {
305+ println ! ( "<no segments>" ) ;
306+ return ;
307+ }
308+
309+ let mut segment_paths: Vec < Option < Vec < Arc < str > > > > = vec ! [ None ; segment_map. len( ) ] ;
310+ let root_layout = self . 0 . layout ( ) . clone ( ) ;
311+
312+ let mut queue =
313+ VecDeque :: < ( Vec < Arc < str > > , LayoutRef ) > :: from_iter ( [ ( Vec :: new ( ) , root_layout) ] ) ;
314+ while !queue. is_empty ( ) {
315+ let ( path, layout) = queue. pop_front ( ) . vortex_expect ( "queue is not empty" ) ;
316+ for segment in layout. segment_ids ( ) {
317+ segment_paths[ * segment as usize ] = Some ( path. clone ( ) ) ;
332318 }
319+
320+ for ( child_layout, child_name) in layout
321+ . children ( )
322+ . vortex_expect ( "Failed to deserialize children" )
323+ . into_iter ( )
324+ . zip ( layout. child_names ( ) )
325+ {
326+ let child_path = path. iter ( ) . cloned ( ) . chain ( [ child_name] ) . collect ( ) ;
327+ queue. push_back ( ( child_path, child_layout) ) ;
328+ }
329+ }
330+
331+ // Find the largest values for formatting
332+ let max_offset = segment_map. last ( ) . vortex_expect ( "non-empty" ) . offset ;
333+ let max_length = segment_map
334+ . iter ( )
335+ . map ( |s| s. length )
336+ . max ( )
337+ . vortex_expect ( "non-empty" ) ;
338+ let max_alignment = segment_map
339+ . iter ( )
340+ . map ( |s| s. alignment )
341+ . max ( )
342+ . vortex_expect ( "non-empty" ) ;
343+
344+ // Calculate all widths
345+ let offset_width = max_offset. to_string ( ) . len ( ) ;
346+ let end_width = ( max_offset + max_length as u64 ) . to_string ( ) . len ( ) ;
347+ let length_width = max_length. to_string ( ) . len ( ) . max ( 6 ) ;
348+ let alignment_width = max_alignment. to_string ( ) . len ( ) . max ( 5 ) ;
349+ let index_width = segment_paths. len ( ) . to_string ( ) . len ( ) ;
350+
351+ // Print header
352+ println ! (
353+ "{:>index_w$} {:>offset_w$}..{:<end_w$} {:>length_w$} {:>align_w$} Path" ,
354+ "#" ,
355+ "Start" ,
356+ "End" ,
357+ "Length" ,
358+ "Align" ,
359+ index_w = index_width,
360+ offset_w = offset_width,
361+ end_w = end_width,
362+ length_w = length_width,
363+ align_w = alignment_width,
364+ ) ;
365+
366+ for ( i, name) in segment_paths. iter ( ) . enumerate ( ) {
367+ let segment = & segment_map[ i] ;
368+ let end_offset = segment. offset + segment. length as u64 ;
369+
370+ print ! (
371+ "{:>index_w$} {:>offset_w$}..{:<end_w$} " ,
372+ i,
373+ segment. offset,
374+ end_offset,
375+ index_w = index_width,
376+ offset_w = offset_width,
377+ end_w = end_width,
378+ ) ;
379+ print ! (
380+ "{:>length_w$} {:>align_w$} " ,
381+ segment. length,
382+ * segment. alignment,
383+ length_w = length_width,
384+ align_w = alignment_width,
385+ ) ;
386+ println ! (
387+ "{}" ,
388+ match name. as_ref( ) {
389+ Some ( path) => format!( "{}" , path. iter( ) . format( "." ) ) ,
390+ None => "<missing>" . to_string( ) ,
391+ }
392+ ) ;
333393 }
334394 }
335395}
0 commit comments