off64 0.9.0

Read from and write to byte slices with u64 offsets
Documentation
#!/usr/bin/env python

from _genutil import ManyTraits

rs_header = """
use crate::*;

// Short convenient macros for converting between int types without using the unsafe `as` operator.
#[macro_export]
macro_rules! isz {
  ($v:expr) => {
    isize::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! i8 {
  ($v:expr) => {
    i8::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! i16 {
  ($v:expr) => {
    i16::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! i32 {
  ($v:expr) => {
    i32::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! i64 {
  ($v:expr) => {
    i64::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! usz {
  ($v:expr) => {
    usize::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! u8 {
  ($v:expr) => {
    u8::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! u16 {
  ($v:expr) => {
    u16::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! u32 {
  ($v:expr) => {
    u32::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! u64 {
  ($v:expr) => {
    u64::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! i128 {
  ($v:expr) => {
    i128::try_from($v).unwrap()
  };
}
#[macro_export]
macro_rules! u128 {
  ($v:expr) => {
    u128::try_from($v).unwrap()
  };
}
"""

mt = ManyTraits("", "Int")

rs_create = ""

# Add support for u8 and i8 (no endianness)
for s in ['i', 'u']:
  mt.add_read_methods(lambda async_, await_, s=s: f"""
    {async_} fn read_{s}8_at(&'a self, offset: u64) -> {s}8 {{
      self.read_at(offset, 1){await_}.as_ref()[0] as {s}8
    }}
  """)

  mt.add_write_methods(lambda async_, await_, mut, s=s: f"""
    {async_} fn write_{s}8_at(&{mut} self, offset: u64, value: {s}8) {{
      self.write_at(offset, &[value as u8]){await_};
    }}
  """)

  rs_create += f"""
    pub fn create_{s}8(value: {s}8) -> [u8; 1] {{
      [value as u8]
    }}
  """

# Add support for 16-128 bit types (with endianness)
for b in [16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128]:
  bytes = b // 8
  if b <= 16:
    w = 16
  elif b <= 32:
    w = 32
  elif b <= 64:
    w = 64
  else:
    w = 128
  width_bytes = w // 8
  for s in ['i', 'u']:
    for e in ['b', 'l']:
      (buf_start, buf_end) = (width_bytes - bytes, width_bytes) if e == 'b' else (0, bytes)
      mt.add_read_methods(lambda async_, await_: f"""
        {async_} fn read_{s}{b}_{e}e_at(&'a self, offset: u64) -> {s}{w} {{
          let mut buf = [0u8; {width_bytes}];
          buf[{buf_start}..{buf_end}].copy_from_slice(
            self.read_at(offset, {bytes}){await_}.as_ref(),
          );
          {s}{w}::from_{e}e_bytes(buf)
        }}
      """)

      mt.add_write_methods(lambda async_, await_, mut: f"""
        {async_} fn write_{s}{b}_{e}e_at(&{mut} self, offset: u64, value: {s}{w}) {{
          let buf = value.to_{e}e_bytes();
          self.write_at(offset, &buf[{buf_start}..{buf_end}]){await_};
        }}
      """)

      rs_create += f"""
        pub fn create_{s}{b}_{e}e(value: {s}{w}) -> [u8; {bytes}] {{
          let buf = value.to_{e}e_bytes();
          buf[{buf_start}..{buf_end}].try_into().unwrap()
        }}
      """

with open("int.rs", "w") as f:
  f.write(rs_header)
  f.write(mt.output())
  f.write(rs_create)