Kilobyte :: 1024;
Megabyte :: 1024 * Kilobyte;
Gigabyte :: 1024 * Megabyte;

DefaultAlign :: size_of(*void);

/// MemEqual checks the equality of two pieces of memory.
///
/// Note: MemEqual will panic if size_in_bytes is negative.
MemEqual :: (p1: *void, p2: *void, size_in_bytes: int) -> bool {
   if size_in_bytes < 0
    { Panic("jc: size_in_bytes cannot be negative"); }
   return memcmp(p1, p2, size_in_bytes) == 0; // Provided by Preload
}

/// MemCopy copies the memory of src to dst.
///
/// Note: MemCopy will panic if size_in_bytes is negative.
MemCopy :: (dst: *void, src: *void, size_in_bytes: int) {
   if size_in_bytes < 0
    { Panic("jc: size_in_bytes cannot be negative"); }
   memcpy(dst, src, size_in_bytes); // Provided by Preload
}

/// MemOverwrite overwites the memory of p with value.
///
/// Note: MemOverwrite will panic if size_in_bytes is negative.
MemOverwrite :: (p: *void, size_in_bytes: int, value: u8 = 0) {
   if size_in_bytes < 0
    { Panic("jc: size_in_bytes cannot be negative"); }
   memset(p, value, size_in_bytes); // Provided by preload
}

/// MemZero zeroes the memory of p.
///
/// Note: MemZero will panic if size_in_bytes is negative.
MemZero :: (p: *void, size_in_bytes: int) {
   MemOverwrite(p, size_in_bytes, 0);
}

/// MemZero zeroes the memory of p.
///
/// Note: MemZero will not call the initializer for aggregate types,
/// so you may want MemReset instead.
MemZero :: (p: *$T) {
   MemOverwrite(p, size_of(T), 0);
}

/// MemReset resets the memory of p, as if it was just instantiated.
///
/// Note: MemReset will call the initializer for aggregate types, so you
/// may want MemZero instead.
MemReset :: (p: *$T) {
   initializer :: initializer_of(T);
   #if initializer {
      inline initializer(p);
   }
   else {
      inline MemZero(p);
   }
}

MemAligned :: (p: *void, align: int = DefaultAlign) -> bool {
   return Aligned(p.(int), align);
}

MemAlignForward :: (p: *void, align: int = DefaultAlign) -> *void {
   return AlignForward(p.(int), align).(*void);
}

MemAlignBackward :: (p: *void, align: int = DefaultAlign) -> *void {
   return AlignBackward(p.(int), align).(*void);
}

Aligned :: (a: int, align: int = DefaultAlign) -> bool {
   return (a & (align - 1)) == 0;
}

AlignForward :: (a: int, align: int = DefaultAlign) -> int {
   Assert(PowerOfTwo(align), "jc: must be a power of two");
   return (a + align - 1) & ~(align - 1);
}

AlignBackward :: (a: int, align: int = DefaultAlign) -> int {
   Assert(PowerOfTwo(align), "jc: must be a power of two");
   return a & ~(align - 1);
}


PowerOfTwo :: (x: int) -> bool {
   if x == 0 return false;
   return x & (x - 1) == 0;
}

NextPowerOfTwo :: (x: int) -> int #no_aoc {
   Assert(PowerOfTwo(x), "jc: must be a power of two");

   // Bit twiddling hacks next power of two
   x |= x >> 1;
   x |= x >> 2;
   x |= x >> 4;
   x |= x >> 8;
   x |= x >> 16;
   x |= x >> 32;

   return x + 1;
}

TrySetAllocator :: (thing: *$T, allocator := context.allocator) #modify {
   info := T.(*Type_Info_Struct);

   ok := false;
   if info.type == .STRUCT
    { ok = true; }

   if ok for info.members if it.name == "allocator" && it.type == Allocator.(*Type_Info) {
      ok = true;
      break;
   }

   return ok, "can only set allocator on struct with an allocator field or dynamic array";
} #expand {
   if thing.allocator.proc == null {
      thing.allocator = allocator;
   }
}

TrySetAllocator :: (array: *[..]$T, allocator := context.allocator) #expand {
   if array.allocator.proc == null {
      array.allocator = allocator;
   }
}
