Rust Lessons

Learn Rust by staring at code.

Free Space for a drive and check on file’s allocation policy in place in OS

Rust

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580

use std::fs::{self, File, remove_file};
use std::io::{self, Write, Seek, SeekFrom};
use std::path::Path;

fn main() -> io::Result<()> {
    loop {
        println!("Enter file length in bytes (0 to quit):");
        let mut input = String::new();
        io::stdin().read_line(&mut input)?;
        let file_len: u64 = input.trim().parse().unwrap_or(0);
        
        if file_len == 0 {
            break;
        }

        let file_path = Path::new("temp_test_file");
        let midpoint = file_len / 2;

        report_space("Before file creation")?;

        // Create file
        let mut file = File::create(file_path)?;
        report_space("After file creation")?;

        // Set file length
        file.set_len(file_len)?;
        report_space("After setting file length")?;

        // Write to midpoint
        file.seek(SeekFrom::Start(midpoint))?;
        file.write_all(&[0u8; 256])?;
        report_space("After writing to middle")?;

        // Cleanup
        drop(file);
        remove_file(file_path)?;
        println!("----------------------------------------\n");
    }

    println!("\nEnd of FreeSpace demonstration");
    Ok(())
}

#[cfg(windows)]
fn report_space(stage: &str) -> io::Result<()> {
    use std::ffi::OsStr;
    use std::os::windows::ffi::OsStrExt;
    use std::mem::MaybeUninit;

    #[repr(C)]
    #[derive(Debug)]
    struct ULARGE_INTEGER {
        quad_part: u64,
    }

    extern "system" {
        fn GetDiskFreeSpaceExW(
            lpDirectoryName: *const u16,
            lpFreeBytesAvailableToCaller: *mut ULARGE_INTEGER,
            lpTotalNumberOfBytes: *mut ULARGE_INTEGER,
            lpTotalNumberOfFreeBytes: *mut ULARGE_INTEGER,
        ) -> i32;
    }

    let path = Path::new(".");
    let mut path_utf16: Vec<u16> = OsStr::new(path).encode_wide().chain(Some(0)).collect();

    let mut free_bytes = MaybeUninit::<ULARGE_INTEGER>::uninit();
    let mut total_bytes = MaybeUninit::<ULARGE_INTEGER>::uninit();
    let mut total_free = MaybeUninit::<ULARGE_INTEGER>::uninit();

    let success = unsafe {
        GetDiskFreeSpaceExW(
            path_utf16.as_ptr(),
            free_bytes.as_mut_ptr(),
            total_bytes.as_mut_ptr(),
            total_free.as_mut_ptr(),
        )
    };

    if success == 0 {
        return Err(io::Error::last_os_error());
    }

    let free_bytes = unsafe { free_bytes.assume_init().quad_part };
    let total_bytes = unsafe { total_bytes.assume_init().quad_part };
    let used_space = total_bytes - free_bytes;

    print_space(stage, total_bytes, used_space, free_bytes);
    Ok(())
}

#[cfg(unix)]
fn report_space(stage: &str) -> io::Result<()> {
    use std::os::unix::ffi::OsStrExt;
    use libc::{statvfs, c_char};
    use std::ffi::CString;

    let path = Path::new(".");
    let c_path = CString::new(path.as_os_str().as_bytes()).unwrap();

    let mut stat: libc::statvfs = unsafe { std::mem::zeroed() };
    let result = unsafe { statvfs(c_path.as_ptr() as *const c_char, &mut stat) };

    if result != 0 {
        return Err(io::Error::last_os_error());
    }

    let block_size = stat.f_frsize as u64;
    let total_bytes = stat.f_blocks * block_size;
    let free_bytes = stat.f_bavail * block_size;
    let used_space = total_bytes - free_bytes;

    print_space(stage, total_bytes, used_space, free_bytes);
    Ok(())
}

fn print_space(stage: &str, total: u64, used: u64, free: u64) {
    // Function (1): `print_space`
    // Purpose: Prints formatted information about disk space usage.
    // Input Parameters:
    //     `stage`: A string slice describing the current stage or context.
    //     `total`: Total disk space in bytes.
    //     `used`: Used disk space in bytes.
    //     `free`: Free disk space in bytes.
    // Output: Prints formatted output to the console.
    // Return Value: None.
    // Core Concept: Formats and displays disk space statistics,
    // demonstrating basic string formatting and numeric output in Rust.
    // Functionality Breakdown:
    //     1. Prints the stage with centered alignment.
    //     2. Prints total, used, and free space in bytes, comma-formatted.
    //     3. Prints total and free space in gigabytes, formatted to two
    //     decimal places.
    // Nested Layers:
    //     Layer 1: Function Definition and Parameter Handling
    //         - Defines the function signature and input parameter types.
    //         - Handles string slice and unsigned 64-bit integer inputs.
    //         - Implies a context where disk space information is relevant
    //         and needs to be displayed.
    //     Layer 2: String Formatting and Output
    //         - Uses `println!` macro for formatted output.
    //         - Leverages format specifiers for alignment and numeric
    //         formatting.
    //         - Calls `format_num` (2) for comma-separated number formatting.
    //         - Calls `bytes_to_gb` (3) for unit conversion (not provided,
    //          assumed to exist).
    //     Layer 3: Data Presentation and Unit Conversion
    //         - Presents data in both bytes and gigabytes for readability.
    //         - Assumes `bytes_to_gb` (3) performs a floating-point division
    //          by 1024^3.
    //         - Implies an audience familiar with both byte and gigabyte units.
    // Implications:
    //     - The function assumes the existence of a `bytes_to_gb` (3)
    //      function.
    //     - The output is intended for human consumption, prioritizing
    //      readability.
    //     - The function does not perform any error handling or input
    //      validation.
    println!("\n{:^20} status:", stage);
    // Variable (4): `stage`.
    // Purpose: Represents the current stage or context of the operation.
    // Data Type: `&str` (string slice).
    // Value Domain: Any valid UTF-8 string.
    // Theoretical Minimum Value: "" (empty string).
    // Theoretical Maximum Value: Theoretically unbounded, practically
    // limited by available memory.
    // Memory Footprint: String slices do not own the underlying data,
    // they only hold a pointer to the data and its length.
    // Architecture: The size of a `&str` is the size of a pointer plus
    // the size of a `usize` (for the length). Typically 16 bytes on 64-bit
    // architectures and 8 bytes on 32-bit architectures.
    // Edge Cases:
    //     - Very long strings might affect formatting.
    //     - Non-ASCII characters might affect alignment.
    //     - Empty string is a valid input.
    //     - Input containing only whitespace characters.
    //     - Input with control characters or escape sequences.
    //     - Input exceeding the display width of the terminal.
    // Nested Layers:
    //     Layer 1: String Slice Representation
    //         - `&str` is a borrowed reference to a string, providing a view
    //          into a portion of a string.
    //         - It does not own the underlying data, ensuring memory safety.
    //         - The lifetime of the `&str` must not exceed the lifetime of
    //          the string it refers to.
    //     Layer 2: UTF-8 Encoding
    //         - `&str` represents a sequence of UTF-8 encoded characters.
    //         - Each character can be 1 to 4 bytes in length.
    //         - UTF-8 is a variable-width character encoding, capable of
    //          encoding all valid Unicode code points.
    //     Layer 3: Memory Safety and Borrowing
    //         - Rust's borrow checker ensures that the `&str` does not outlive
    //          the string it borrows from.
    //         - Prevents dangling pointers and ensures memory safety.
    //         - Implies that the string data is owned elsewhere and cannot
    //          be modified through this `&str`.

    println!("{:<15} {:>20} bytes", "Total space:", format_num(total));
    // Variable (5): `total`.
    // Purpose: Represents the total disk space in bytes.
    // Data Type: `u64` (unsigned 64-bit integer).
    // Value Domain: [0, 2^64 - 1].
    // Theoretical Minimum Value: 0.
    // Theoretical Maximum Value: 18,446,744,073,709,551,615.
    // Memory Footprint: 8 bytes on all architectures.
    // Edge Cases:
    //     - Extremely large values might be difficult to format or display.
    //     - Zero value indicates no disk space.
    //     - Maximum value (2^64 - 1) is unlikely in practice but technically
    //      possible.
    //     - Input values representing impossible physical disk sizes.
    //     - Values near the maximum representable limit, approaching
    //      overflow if manipulated further.
    //     - Values that are not multiples of typical disk sector sizes.
    // Nested Layers:
    //     Layer 1: Unsigned Integer Representation
    //         - `u64` represents a non-negative integer using 64 bits.
    //         - Uses binary representation for storage.
    //         - Cannot represent negative values.
    //     Layer 2: Bitwise Operations
    //         - `u64` supports bitwise operations like AND, OR, XOR, NOT,
    //          left shift, and right shift.
    //         - These operations can be used for low-level manipulation of
    //          the value.
    //         - Potential for bit manipulation errors if not handled
    //          carefully.
    //     Layer 3: Overflow Behavior
    //         - In Rust, arithmetic operations on `u64` wrap around on
    //          overflow in release mode.
    //         - In debug mode, overflow will cause a panic.
    //         - Overflow can lead to unexpected results if not accounted for.
    // Mitigation Strategies for Edge Cases:
    //     - Implement input validation to ensure the value is within
    //      realistic bounds for the given system.
    //     - Use error handling mechanisms to gracefully handle overflow
    //      situations.
    //     - Consider using larger data types or specialized libraries for
    //      handling very large numbers if necessary.

    println!("{:<15} {:>20} bytes", "Used space:", format_num(used));
    // Variable (6): `used`.
    // Purpose: Represents the used disk space in bytes.
    // Data Type: `u64` (unsigned 64-bit integer).
    // Value Domain: [0, 2^64 - 1].
    // Theoretical Minimum Value: 0.
    // Theoretical Maximum Value: 18,446,744,073,709,551,615.
    // Memory Footprint: 8 bytes.
    // Edge Cases:
    //     - `used` greater than `total` is logically impossible but should
    //      be handled.
    //     - Zero value indicates no disk space used.
    //     - A value equal to `total` indicates no free space.
    //     - Values significantly larger than the expected usage,
    //      potentially indicating an error in calculation.
    //     - Values very close to the total capacity, leaving little free
    //      space.
    //     - Rapidly changing values in a multithreaded environment without
    //      proper synchronization.
    // Nested Layers:
    //     Layer 1: Relationship with `total` and `free`
    //         - `used` is typically derived from `total` and `free`, or vice
    //          versa.
    //         - The relationship should be: `total` = `used` + `free`.
    //         - Inconsistency in this relationship indicates an error.
    //     Layer 2: Implications for System Operations
    //         - High `used` space can lead to performance degradation.
    //         - Low free space can prevent new files from being created.
    //         - Monitoring `used` space is crucial for system stability.
    //     Layer 3: Potential for Integer Overflow
    //         - If `used` is calculated from other values, there's a
    //          potential for integer overflow.
    //         - Overflow can lead to incorrect calculations and unexpected
    //          behavior.
    //         - Mitigation: Use checked arithmetic operations or saturating
    //          arithmetic to handle overflow gracefully.

    println!("{:<15} {:>20} bytes", "Free space:", format_num(free));
    // Variable (7): `free`.
    // Purpose: Represents the free disk space in bytes.
    // Data Type: `u64` (unsigned 64-bit integer).
    // Value Domain: [0, 2^64 - 1].
    // Theoretical Minimum Value: 0.
    // Theoretical Maximum Value: 18,446,744,073,709,551,615.
    // Memory Footprint: 8 bytes.
    // Edge Cases:
    //     - Zero value indicates no free space, potentially halting system
    //      operations.
    //     - `free` greater than `total` is logically impossible.
    //     - Very small values indicate limited capacity for new data.
    //     - Values significantly larger than expected, indicating potential
    //      errors in disk space reporting.
    //     - Negative values if calculated incorrectly due to overflow.
    //     - Inconsistent values reported by different tools or APIs due to
    //      timing issues or caching mechanisms.
    // Nested Layers:
    //     Layer 1: Relationship with `total` and `used`
    //         - `free` is typically derived from `total` and `used`.
    //         - Should satisfy: `total` = `used` + `free`.
    //         - Discrepancies indicate errors in calculation or reporting.
    //     Layer 2: Impact on System Performance
    //         - Low `free` space can severely impact performance.
    //         - Can lead to fragmentation and slow down file operations.
    //         - Operating systems often reserve a portion of disk space,
    //          affecting the actual usable `free` space.
    //     Layer 3: Implications for File System Operations
    //         - `free` space is crucial for creating and extending files.
    //         - File systems may have limitations on how they allocate
    //          `free` space.
    //         - Different file systems may report `free` space differently.

    println!("{:<15} {:>20.2} GB", "Total:", bytes_to_gb(total));
    // Function Call (3): `bytes_to_gb` (Assumed)
    // Purpose: Converts bytes to gigabytes.
    // Input: `u64` (bytes).
    // Output: `f64` or similar (gigabytes).
    // Core Concept: Unit conversion, likely involves division by 1024^3.
    // Potential Implementation: `fn bytes_to_gb(bytes: u64) -> f64 {
    // bytes as f64 / (1024.0 * 1024.0 * 1024.0) }`
    // Edge Cases for `bytes_to_gb`:
    //     - Input of 0 should return 0.
    //     - Very large inputs might lose precision due to floating-point
    //      representation.
    //     - Potential for rounding errors in the conversion.
    //     - Different interpretations of "gigabyte" (1000^3 vs 1024^3).
    //     - Input values that result in fractional gigabytes, requiring
    //      rounding for display.
    //     - Input values exceeding the maximum representable value for
    //      `f64`, leading to infinity or NaN.

    println!("{:<15} {:>20.2} GB", "Free:", bytes_to_gb(free));
    // Redundant call to `bytes_to_gb` with `free`.
}

fn format_num(n: u64) -> String {
    // Function (2): `format_num`
    // Purpose: Formats an unsigned 64-bit integer with comma separators.
    // Input: `n` - The number to format.
    // Output: A formatted string representation of the number.
    // Core Concept: String manipulation and numeric formatting. Adds commas
    // as thousands separators for improved readability.
    // Functionality Breakdown:
    //     1. Converts the `u64` to a `String`.
    //     2. Iterates through the characters of the string in reverse.
    //     3. Inserts a comma every three digits.
    //     4. Reverses the result to obtain the correct order.
    // Nested Layers:
    //     Layer 1: Number to String Conversion
    //         - `n.to_string()` converts the `u64` to its decimal string
    //          representation.
    //         - This involves converting the internal binary representation
    //          to a base-10 string.
    //         - The resulting string is allocated on the heap.
    //     Layer 2: Reverse Iteration and Comma Insertion
    //         - Iterating in reverse simplifies the logic for inserting
    //          commas.
    //         - `count` variable tracks the position from the end of the
    //          string.
    //         - Conditional logic ensures commas are only inserted at
    //          appropriate positions.
    //     Layer 3: String Reversal and Collection
    //         - `result.chars().rev().collect()` reverses the string back to
    //          the original order.
    //         - `collect()` gathers the characters into a new `String`.
    //         - Involves creating a new string and copying characters.
    // Implications:
    //     - The function allocates memory on the heap for the intermediate
    //      and final strings.
    //     - The performance might be suboptimal for very large numbers due
    //      to multiple string manipulations.
    //     - The function assumes the standard US-style number formatting
    //      with commas as thousands separators.

    let num_str = n.to_string();
    // Variable (8): `num_str`.
    // Purpose: Stores the string representation of the input number `n`.
    // Data Type: `String` (owned string).
    // Value Domain: Any valid string representation of a `u64` in base 10.
    // Theoretical Minimum Value: "0".
    // Theoretical Maximum Value: "18446744073709551615".
    // Memory Footprint:  Variable, depends on the length of the string.
    // Each character is a UTF-8 encoded, taking 1-4 bytes per character.
    // Additionally, `String` has an overhead for managing the heap-allocated
    // data (pointer, length, capacity).
    // Edge Cases:
    //     - Input of 0 results in "0".
    //     - Maximum `u64` value results in "18446744073709551615".
    //     - Large numbers will result in longer strings, consuming more
    //      memory.
    //     - The string representation of very large numbers might be
    //      difficult to read without separators.
    //     - The conversion process itself might have limitations based on
    //      the implementation of `to_string()`.
    //     - Potential memory allocation failures if the system is out of
    //      memory and the number is extremely large.

    let mut result = String::new();
    // Variable (9): `result`.
    // Purpose: Stores the formatted number string being built.
    // Data Type: `String` (owned string).
    // Value Domain:  Any valid UTF-8 string, but intended to be a
    // comma-separated representation of a number.
    // Theoretical Minimum Value: "" (empty string).
    // Theoretical Maximum Value:  Theoretically unbounded, practically
    // limited by available memory and the maximum length of the formatted
    // number.
    // Memory Footprint:  Initially, it's an empty string with a small
    // overhead. As characters and commas are added, it will grow dynamically
    // on the heap.
    // Edge Cases:
    //     - The number of commas inserted depends on the magnitude of the
    //      input number.
    //     - For very large numbers, the resulting string can become quite
    //      long.
    //     - Potential for memory allocation failures during string growth
    //      if the system is low on memory.
    //     - If the input number is zero, the result will be "0" without any
    //      commas.
    //     - The growth pattern of the string might not be linear due to
    //      reallocations when the capacity is exceeded.
    //     - The specific growth strategy depends on the implementation of
    //      `String` and its allocator.

    let mut count = 0;
    // Variable (10): `count`.
    // Purpose: Tracks the number of digits processed from the right.
    // Data Type: `u64`.
    // Value Domain: [0, 2^64 - 1]. Practically, it will be much smaller,
    // limited by the number of digits in the input number.
    // Theoretical Minimum Value: 0.
    // Theoretical Maximum Value:  Theoretically 18,446,744,073,709,551,615,
    // but practically limited by the number of digits in `n`.
    // Memory Footprint: 8 bytes.
    // Edge Cases:
    //     - If the input number has a number of digits that is a multiple
    //      of 3, the last group will not have a comma prepended.
    //     - For single-digit numbers, `count` will only reach 1.
    //     - For very large numbers, `count` will correspond to the total
    //      number of digits in the number.
    //     - Potential for integer overflow if the input number has more
    //      than 2^64 - 1 digits (highly unlikely).
    //     - The maximum value of `count` is limited by the maximum length
    //      of the string representation of the input number.
    //     - If the input number were somehow modified externally during
    //      the iteration, `count` might not accurately reflect the position.

    for c in num_str.chars().rev() {
        // Control Flow (11): `for` loop
        // Purpose: Iterates over the characters of `num_str` in reverse
        // order.
        // Iteration: The loop processes each character from the end of the
        // string to the beginning.
        // Termination: The loop terminates when all characters have been
        // processed.
        // Edge Cases:
        //     - Empty string: The loop will not execute.
        //     - String with only one character: The loop will execute once.
        //     - Very long strings: The loop will iterate many times,
        //      potentially impacting performance.
        //     - If the string is modified concurrently during iteration,
        //      the behavior is undefined.
        //     - The performance of the loop depends on the efficiency of
        //      the `chars()` and `rev()` iterators.
        //     - The loop's behavior is undefined if the string contains
        //      invalid UTF-8 sequences.
        // Nested Layers:
        //     Layer 1: `chars()` Iterator
        //         - `chars()` creates an iterator over the Unicode scalar
        //          values of the string.
        //         - Each iteration yields a `char`.
        //         - The iterator handles UTF-8 decoding.
        //     Layer 2: `rev()` Iterator Adapter
        //         - `rev()` reverses the direction of the iterator.
        //         - It adapts the underlying `chars()` iterator to yield
        //          elements in reverse order.
        //         - `rev()` itself does not allocate new memory. It just
        //          changes the iteration direction.
        //     Layer 3: Loop Body Execution
        //         - For each character, the loop body is executed.
        //         - The loop body manipulates `result` and `count`.
        //         - The order of execution is crucial for the correct
        //          formatting.

        if count % 3 == 0 && count != 0 {
            // Control Flow (12): `if` statement
            // Purpose: Determines whether to insert a comma.
            // Condition: A comma is inserted if `count` is a multiple of 3
            // and not zero.
            // Edge Cases:
            //     - `count` is 0: No comma is inserted.
            //     - `count` is a multiple of 3: A comma is inserted.
            //     - `count` is not a multiple of 3: No comma is inserted.
            //     - The first group of digits (from the right) will never
            //      have a comma prepended, even if it has three digits.
            //     - The condition ensures that commas are only inserted
            //      between groups of digits, not at the beginning.
            //     - The behavior is consistent for all valid input values
            //      of `count`.

            result.push(',');
            // String Manipulation (13): `result.push(',')`
            // Purpose: Appends a comma to the `result` string.
            // Operation: Modifies `result` in-place by adding a comma
            // character.
            // Edge Cases:
            //     - `result` is empty: The comma becomes the first character.
            //     - `result` already has content: The comma is appended to
            //      the end.
            //     - Repeated calls to `push` will keep appending commas.
            //     - Potential for memory reallocation if `result`'s
            //      capacity is exceeded.
            //     - The performance of `push` depends on the implementation
            //      of `String` and its dynamic resizing strategy.
            //     - If the system is out of memory, `push` might fail to
            //      allocate more memory for the string.
        }
        result.push(c);
        // String Manipulation (14): `result.push(c)`
        // Purpose: Appends the current character `c` to the `result` string.
        // Operation: Modifies `result` in-place.
        // Edge Cases:
        //     - `result` is empty: `c` becomes the first character.
        //     - `result` already has content: `c` is appended to the end.
        //     - Repeated calls will append multiple characters.
        //     - Memory reallocation might occur if `result`'s capacity is
        //      exceeded.
        //     - The performance depends on `String`'s implementation.
        //     - If `c` is a multi-byte UTF-8 character, it will be appended
        //      correctly as a single unit.

        count += 1;
        // Variable Update (15): `count += 1`
        // Purpose: Increments the digit counter.
        // Operation: Adds 1 to `count`.
        // Edge Cases:
        //     - `count` is at its maximum value: Overflow will occur (in
        //      release mode, it will wrap around; in debug mode, it will
        //      panic).
        //     - Overflow is unlikely in this specific scenario but
        //      theoretically possible.
        //     - The increment ensures that `count` tracks the number of
        //      digits processed from the right.
        //     - The correctness of the comma placement logic depends on
        //      this increment.
        //     - If the increment is accidentally skipped or duplicated,
        //      the formatting will be incorrect.
    }

    result.chars().rev().collect()
    // String Manipulation (16): `result.chars().rev().collect()`
    // Purpose: Reverses the `result` string to obtain the correct order.
    // Operation:
    //     1. `result.chars()`: Creates an iterator over the characters of
    //      `result`.
    //     2. `.rev()`: Reverses the iterator.
    //     3. `.collect()`: Collects the characters into a new `String`.
    // Edge Cases:
    //     - `result` is empty: Returns an empty string.
    //     - `result` has one character: Returns a string with the same
    //      character.
    //     - `result` has multiple characters: Returns a new string with
    //      the characters in reversed order.
    //     - Memory allocation occurs during `collect()` to create the new
    //      reversed string.
    //     - The performance depends on the length of `result` and the
    //      efficiency of the iterators and `collect()`.
    //     - If `result` contains multi-byte UTF-8 characters, they will
    //      be reversed correctly as single units.
}

fn bytes_to_gb(bytes: u64) -> f64 {
    bytes as f64 / (1024.0 * 1024.0 * 1024.0)
}





C++ Implementation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// C++ implementation will be added here

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>

void ReportError(LPCTSTR msg, DWORD exitCode, BOOL exitProgram);
void ReportSpace(LPCTSTR Message);

int _tmain(int argc, LPTSTR argv[]) {
    HANDLE hFile;
    LARGE_INTEGER FileLen, FileLenH;
    BYTE Buffer[256];
    OVERLAPPED ov = {0};
    DWORD nWrite;

    while (1) {
        FileLen.QuadPart = 0;
        _tprintf(_T("Enter file length in bytes (0 to quit): "));
        _tscanf_s(_T("%I64d"), &FileLen.QuadPart);
        
        if (FileLen.QuadPart == 0)
            break;
        
        _tprintf(_T("\nRequested file size: %,20I64d bytes\n"), FileLen.QuadPart);
        FileLenH.QuadPart = FileLen.QuadPart / 2;

        ReportSpace(_T("Before file creation"));

        hFile = CreateFile(_T("TempTestFile"), GENERIC_READ | GENERIC_WRITE, 0, NULL,
                           CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
            ReportError(_T("Cannot create TempTestFile"), 2, TRUE);
        ReportSpace(_T("After file creation"));

        if (!SetFilePointerEx(hFile, FileLen, NULL, FILE_BEGIN))
            ReportError(_T("Cannot set file pointer"), 3, TRUE);

        if (!SetEndOfFile(hFile))
            ReportError(_T("Cannot set end of file"), 4, TRUE);
        ReportSpace(_T("After setting file length"));

        ov.Offset = FileLenH.LowPart;
        ov.OffsetHigh = FileLenH.HighPart;

        if (!WriteFile(hFile, Buffer, sizeof(Buffer), &nWrite, &ov))
            ReportError(_T("Cannot write to middle of file"), 5, TRUE);
        ReportSpace(_T("After writing to middle"));

        CloseHandle(hFile);
        DeleteFile(_T("TempTestFile"));
        _tprintf(_T("\n----------------------------------------\n"));
    }
    
    _tprintf(_T("\nEnd of FreeSpace demonstration\n"));
    return 0;
}

void ReportError(LPCTSTR msg, DWORD exitCode, BOOL exitProgram) {
    LPVOID lpMsgBuf;
    DWORD dw = GetLastError();

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
        NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR)&lpMsgBuf, 0, NULL);

    _tprintf(_T("\nERROR [%d]: %s\n"), dw, (LPCTSTR)lpMsgBuf);
    LocalFree(lpMsgBuf);

    if (exitProgram) {
        ExitProcess(exitCode);
    }
}

void ReportSpace(LPCTSTR Message) {
    ULARGE_INTEGER FreeBytes, TotalBytes, NumFreeBytes;
    const double GB = 1024.0 * 1024.0 * 1024.0;

    if (!GetDiskFreeSpaceEx(NULL, &FreeBytes, &TotalBytes, &NumFreeBytes))
        ReportError(_T("Cannot get free space"), 1, TRUE);

    _tprintf(_T("\n%25s status:\n"), Message);
    _tprintf(_T("  Total disk space:   %12.2f GB\n"), (double)TotalBytes.QuadPart / GB);
    _tprintf(_T("  Actual free space:  %12.2f GB\n"), (double)NumFreeBytes.QuadPart / GB);
    _tprintf(_T("  Available to user:  %12.2f GB\n"), (double)FreeBytes.QuadPart / GB);
}

Exercise

Let us implement this for small disks and observe, in C++23 or C++20.