class_docstr: Writes given dataset into FFCV format (.beton).
  Supports indexable objects (e.g., PyTorch Datasets) and webdataset.
  
  Parameters
  ----------
  fname: str
      File name to store dataset in FFCV format (.beton)
  fields : Mapping[str, Field]
      Map from keys to Field's (order matters!)
  page_size : int
      Page size used internally
  num_workers : int
      Number of processes to use
code: def prepare(self):
      with open(self.fname, "wb") as fp:
          # Prepare the header data
          header = np.zeros(1, dtype=HeaderType)[0]
          header["version"] = CURRENT_VERSION
          header["num_samples"] = self.num_samples
          header["num_fields"] = len(self.fields)
          header["page_size"] = self.page_size
          self.header = header
  
          # We will write the header at the end because we need to know where
          # The memory allocation table is in the file
          # We still write it here to make space for it later
          fp.write(self.header.tobytes())
  
          # Writes the information about the fields
          fields_descriptor = np.zeros(
              len(self.fields), dtype=FieldDescType
          )
          field_type_to_type_id = {
              v: k for (k, v) in TYPE_ID_HANDLER.items()
          }
  
          fieldname_max_len = fields_descriptor[0]["name"].shape[0]
  
          for i, (name, field) in enumerate(self.fields.items()):
              type_id = field_type_to_type_id.get(type(field), 255)
              encoded_name = name.encode("ascii")
              encoded_name = np.frombuffer(encoded_name, dtype="<u1")
              actual_length = min(fieldname_max_len, len(encoded_name))
              fields_descriptor[i]["type_id"] = type_id
              fields_descriptor[i]["name"][
                  :actual_length
              ] = encoded_name[:actual_length]
              fields_descriptor[i]["arguments"][:] = field.to_binary()[
                  0
              ]
  
          fp.write(fields_descriptor.tobytes())
  
      total_metadata_size = (
          self.num_samples * self.metadata_type.itemsize
      )
  
      # Shared memory for all the writers to fill the information
      self.metadata_sm = 3
      self.metadata_start = (
          HeaderType.itemsize + fields_descriptor.nbytes
      )
  
      self.metadata_sm = shared_memory.SharedMemory(
          create=True, size=total_metadata_size
      )
  
      self.data_region_start = self.metadata_start + total_metadata_size
  
imports: from functools import partial
  from typing import Callable, List, Mapping
  from os import SEEK_END, path
  import numpy
  from time import sleep
  import ctypes
  from multiprocessing import shared_memory, cpu_count, Queue, Process, Value
  from tqdm import tqdm
  from tqdm.contrib.concurrent import thread_map
  from .utils import chunks, is_power_of_2
  from .fields.base import Field
  from .memory_allocator import MemoryAllocator
  from .types import TYPE_ID_HANDLER, get_metadata_type, HeaderType, FieldDescType, CURRENT_VERSION, ALLOC_TABLE_TYPE
label_desc: Use image node_id 2 for calling the DatasetWriter obj's underlying member method code with example usage: obj.prepare() without return types