Repository: qdrant/qdrant
File: lib/gridstore/src/tracker.rs (Rust)

Code before:
use std::path::{Path, PathBuf};

use ahash::{AHashMap, AHashSet};
use memmap2::MmapMut;
use memory::madvise::{Advice, AdviceSetting, Madviseable};
#[expect(deprecated, reason = "legacy code")]
use memory::mmap_ops::{
    create_and_ensure_length, open_write_mmap, transmute_from_u8, transmute_to_u8,
};
use smallvec::SmallVec;

use crate::Result;
use crate::error::GridstoreError;

pub type PointOffset = u32;
pub type BlockOffset = u32;
pub type PageId = u32;

const TRACKER_MEM_ADVICE: Advice = Advice::Random;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct ValuePointer {
    /// This is an artificial field to make `Option<ValuePointer>` to have a stable ABI.
    pub _discriminant: NonZeroU8,
    pub _padding: [u8; 3],

    /// Which page the value is stored in
    pub page_id: PageId,

    /// Start offset (in blocks) of the value
    pub block_offset: BlockOffset,

    /// Length in bytes of the value
    pub length: u32,
}

impl ValuePointer {
    pub fn new(page_id: PageId, block_offset: BlockOffset, length: u32) -> Self {
        Self {
            _discriminant: NonZeroU8::new(1).unwrap(),
            _padding: [0, 0, 0],
            page_id,
            block_offset,
            length,
        }
    }
}

/// Pointer updates for a given point offset
///

Review comment (performance):
Note: this change made `ValuePointer` a bit larger (12 -> 16 bytes). I wonder, can it affect performance/runtime memory usage of Gridstore?

Next-token-predictor said not much, tho.

<details><summary>prompt used</summary>

@lib/gridstore/src/tracker.rs

We added `_discriminant` and `_padding` fields to `ValuePointer` to "legalize" layout of `Option<ValuePointer>`.
On-disk representation of `Option<ValuePointer>` didn't changed, so from correctness point of view it's good.

But the question from the performance point of view.
Now `ValuePointer` a bit larger (12->16 bytes). Can it affect the performance and RAM usage of Gridstore?
Check whether it is used in hot loops or large arrays of data.

</details>

---

A bit cleaner alternative option is to "reimplement" option enum like this instead:
```rust
struct ValuePointerOption {
    is_some: u32, // 0 or 1
    value_pointer: ValuePointer,
}
```
Tho the current version of this PR is still good, I think.

Code after:

use ahash::{AHashMap, AHashSet};
use memmap2::MmapMut;
use memory::madvise::{Advice, AdviceSetting, Madviseable};
#[expect(deprecated, reason = "legacy code")]
use memory::mmap_ops::{
    create_and_ensure_length, open_write_mmap, transmute_from_u8, transmute_to_u8,
};
use smallvec::SmallVec;
use zerocopy::FromZeros;

use crate::Result;
use crate::error::GridstoreError;

pub type PointOffset = u32;
pub type BlockOffset = u32;
pub type PageId = u32;

const TRACKER_MEM_ADVICE: Advice = Advice::Random;

/// A type similar to [`std::option::Option`], but with stable layout. It is intended to be compatible with older
/// gridstore files, but it is well-defined, unlike [`std::option::Option`].
///
/// Please note that it uses 32-bit tag and is intended to be used for `ValuePointer` without padding bytes.
///
/// If `T` is a POD type, then `Optional<T>` is a POD type.
#[derive(Copy, Clone, zerocopy::FromBytes)]
#[repr(C)]
struct Optional<T> {
    discriminant: u32,
    value: T,
}

impl<T: FromZeros> From<Option<T>> for Optional<T> {
    fn from(value: Option<T>) -> Self {
        match value {
            Some(value) => Self::some(value),
            None => Self::none(),
        }
    }
}

impl<T: FromZeros> Optional<T> {
    const OPTIONAL_NONE: u32 = 0;
    const OPTIONAL_SOME: u32 = 1;

    /// None value is all zeroes.
    pub fn none() -> Self {
        Self {
            discriminant: Self::OPTIONAL_NONE,
            value: FromZeros::new_zeroed(),