Compare commits
4
Commits
master
..
1af8b5cdfb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1af8b5cdfb | ||
|
|
7febc2d200 | ||
|
|
26e59f32a9 | ||
|
|
a6f1c3701a |
+11
-20
@@ -765,14 +765,11 @@ compute_backdrop_group_work_region :: proc(
|
|||||||
max_x += halo_logical
|
max_x += halo_logical
|
||||||
max_y += halo_logical
|
max_y += halo_logical
|
||||||
|
|
||||||
// Clamp the min corner to 0, but let the max corner's 6σ halo extend past the swapchain edge
|
// Convert to physical pixels and clamp to swapchain bounds.
|
||||||
// into the working texture's unused area (at factor > 1), capped to the texture extent. Keeps
|
phys_min_x := math.max(min_x * dpi, 0)
|
||||||
// the composite's bilinear upsample off the unwritten texels just past a clamped edge.
|
phys_min_y := math.max(min_y * dpi, 0)
|
||||||
downsample_factor := compute_backdrop_downsample_factor(sigma_logical)
|
phys_max_x := math.min(max_x * dpi, f32(swapchain_width))
|
||||||
phys_min_x := max(min_x * dpi, 0)
|
phys_max_y := math.min(max_y * dpi, f32(swapchain_height))
|
||||||
phys_min_y := max(min_y * dpi, 0)
|
|
||||||
phys_max_x := min(max_x * dpi, f32(swapchain_width * downsample_factor))
|
|
||||||
phys_max_y := min(max_y * dpi, f32(swapchain_height * downsample_factor))
|
|
||||||
|
|
||||||
if phys_max_x <= phys_min_x || phys_max_y <= phys_min_y do return 0, 0, 0, 0
|
if phys_max_x <= phys_min_x || phys_max_y <= phys_min_y do return 0, 0, 0, 0
|
||||||
|
|
||||||
@@ -871,18 +868,12 @@ run_backdrop_bracket :: proc(
|
|||||||
working_w := (region_w + downsample_factor - 1) / downsample_factor
|
working_w := (region_w + downsample_factor - 1) / downsample_factor
|
||||||
working_h := (region_h + downsample_factor - 1) / downsample_factor
|
working_h := (region_h + downsample_factor - 1) / downsample_factor
|
||||||
|
|
||||||
// Clamp to the full texture extent (not cached/factor): the working textures are full
|
// Working textures are sized at min factor (2). At factor=4 we have only half the texture
|
||||||
// swapchain res, so a factor-N group's halo can spill into the unused remainder. Writing it
|
// area available in each axis. Clamp to the texture extent for either case.
|
||||||
// keeps the composite's bilinear upsample off unwritten texels at the right/bottom edge.
|
wt_w := pipeline.cached_width / downsample_factor
|
||||||
texture_width := pipeline.cached_width
|
wt_h := pipeline.cached_height / downsample_factor
|
||||||
texture_height := pipeline.cached_height
|
if working_x + working_w > wt_w do working_w = wt_w - working_x
|
||||||
// Skip fully off-screen groups; also guards the unsigned clamps below from underflow.
|
if working_y + working_h > wt_h do working_h = wt_h - working_y
|
||||||
if working_x >= texture_width || working_y >= texture_height {
|
|
||||||
i = group_end
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if working_x + working_w > texture_width do working_w = texture_width - working_x
|
|
||||||
if working_y + working_h > texture_height do working_h = texture_height - working_y
|
|
||||||
if working_w == 0 || working_h == 0 {
|
if working_w == 0 || working_h == 0 {
|
||||||
i = group_end
|
i = group_end
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -730,9 +730,21 @@ flush_deferred_and_close_backdrop_scope :: proc(
|
|||||||
prepare_clay_batch :: proc(
|
prepare_clay_batch :: proc(
|
||||||
base_layer: ^Layer,
|
base_layer: ^Layer,
|
||||||
batch: ^ClayBatch,
|
batch: ^ClayBatch,
|
||||||
|
mouse_wheel_delta: [2]f32,
|
||||||
|
frame_time: f32 = 0,
|
||||||
custom_draw: Custom_Draw = nil,
|
custom_draw: Custom_Draw = nil,
|
||||||
temp_allocator := context.temp_allocator,
|
temp_allocator := context.temp_allocator,
|
||||||
) {
|
) {
|
||||||
|
mouse_pos: [2]f32
|
||||||
|
mouse_flags := sdl.GetMouseState(&mouse_pos.x, &mouse_pos.y)
|
||||||
|
|
||||||
|
// Update clay internals
|
||||||
|
clay.SetPointerState(
|
||||||
|
clay.Vector2{mouse_pos.x - base_layer.bounds.x, mouse_pos.y - base_layer.bounds.y},
|
||||||
|
.LEFT in mouse_flags,
|
||||||
|
)
|
||||||
|
clay.UpdateScrollContainers(true, mouse_wheel_delta, frame_time)
|
||||||
|
|
||||||
layer := base_layer
|
layer := base_layer
|
||||||
command_count := int(batch.cmds.length)
|
command_count := int(batch.cmds.length)
|
||||||
deferred_indices := make([dynamic]i32, 0, 16, temp_allocator)
|
deferred_indices := make([dynamic]i32, 0, 16, temp_allocator)
|
||||||
|
|||||||
@@ -857,15 +857,6 @@ needs_transform :: #force_inline proc(origin: Vec2, rotation: f32) -> bool {
|
|||||||
// ----- Anchors ------------
|
// ----- Anchors ------------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Translate `local` — a point relative to `bounds`'s top-left — into `bounds`'s own coordinate
|
|
||||||
// space by offsetting it by the rectangle's origin. Unlike the anchor helpers above (which
|
|
||||||
// return size-relative offsets and ignore `bounds.x`/`bounds.y`), this uses the rectangle's
|
|
||||||
// position. Handy for drawing inside a sub-region whose rect you were handed — e.g. the
|
|
||||||
// `bounds` passed to a `Custom_Draw` callback: `local_to_bounds(bounds, {cx, cy})`.
|
|
||||||
bound :: #force_inline proc(bounds: Rectangle, local: Vec2) -> Vec2 {
|
|
||||||
return {bounds.x + local.x, bounds.y + local.y}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return Vec2 pixel offsets for use as the `origin` parameter of draw calls.
|
// Return Vec2 pixel offsets for use as the `origin` parameter of draw calls.
|
||||||
// Composable with normal vector +/- arithmetic.
|
// Composable with normal vector +/- arithmetic.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ clay_borders :: proc() {
|
|||||||
bounds = base_layer.bounds,
|
bounds = base_layer.bounds,
|
||||||
cmds = clay.EndLayout(0),
|
cmds = clay.EndLayout(0),
|
||||||
}
|
}
|
||||||
draw.prepare_clay_batch(base_layer, &clay_batch)
|
draw.prepare_clay_batch(base_layer, &clay_batch, {0, 0})
|
||||||
draw.end(gpu, window)
|
draw.end(gpu, window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ hellope_clay :: proc() {
|
|||||||
bounds = base_layer.bounds,
|
bounds = base_layer.bounds,
|
||||||
cmds = clay.EndLayout(0),
|
cmds = clay.EndLayout(0),
|
||||||
}
|
}
|
||||||
draw.prepare_clay_batch(base_layer, &clay_batch)
|
draw.prepare_clay_batch(base_layer, &clay_batch, {0, 0})
|
||||||
draw.end(gpu, window)
|
draw.end(gpu, window)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -372,7 +372,7 @@ hellope_custom :: proc() {
|
|||||||
bounds = base_layer.bounds,
|
bounds = base_layer.bounds,
|
||||||
cmds = clay.EndLayout(0),
|
cmds = clay.EndLayout(0),
|
||||||
}
|
}
|
||||||
draw.prepare_clay_batch(base_layer, &clay_batch, custom_draw = draw_custom)
|
draw.prepare_clay_batch(base_layer, &clay_batch, {0, 0}, custom_draw = draw_custom)
|
||||||
draw.end(gpu, window)
|
draw.end(gpu, window)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+44
-119
@@ -120,52 +120,10 @@ spinlock_try_lock :: #force_inline proc "contextless" (lock: ^Spinlock) -> bool
|
|||||||
return lock_acquired
|
return lock_acquired
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spins until the lock is acquired, relaxing the CPU between attempts.
|
|
||||||
spinlock_lock :: #force_inline proc "contextless" (lock: ^Spinlock) {
|
|
||||||
for !spinlock_try_lock(lock) {
|
|
||||||
intrinsics.cpu_relax()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spinlock_unlock :: #force_inline proc "contextless" (lock: ^Spinlock) {
|
spinlock_unlock :: #force_inline proc "contextless" (lock: ^Spinlock) {
|
||||||
intrinsics.atomic_store_explicit(lock, false, .Release)
|
intrinsics.atomic_store_explicit(lock, false, .Release)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spins until the lock is acquired, then unlocks at the end of the calling scope. Always returns
|
|
||||||
// true so it can guard a critical section from within an `if`:
|
|
||||||
//
|
|
||||||
// if spinlock_guard(&lock) {
|
|
||||||
// // critical section
|
|
||||||
// }
|
|
||||||
@(deferred_in = spinlock_unlock)
|
|
||||||
spinlock_guard :: #force_inline proc "contextless" (lock: ^Spinlock) -> bool {
|
|
||||||
spinlock_lock(lock)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tries to acquire the lock once without spinning. Returns true and unlocks at the end of the
|
|
||||||
// calling scope if acquired, otherwise returns false and does nothing:
|
|
||||||
//
|
|
||||||
// if spinlock_try_guard(&lock) {
|
|
||||||
// // critical section, entered only if the lock was acquired
|
|
||||||
// }
|
|
||||||
@(deferred_in_out = spinlock_try_guard_unlock)
|
|
||||||
spinlock_try_guard :: #force_inline proc "contextless" (lock: ^Spinlock) -> bool {
|
|
||||||
return spinlock_try_lock(lock)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deferred companion of `spinlock_try_guard`; unlocks only when the lock was actually acquired.
|
|
||||||
@(private)
|
|
||||||
spinlock_try_guard_unlock :: #force_inline proc "contextless" (lock: ^Spinlock, locked: bool) {
|
|
||||||
if locked {
|
|
||||||
spinlock_unlock(lock)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lock :: proc {
|
|
||||||
spinlock_lock,
|
|
||||||
}
|
|
||||||
|
|
||||||
try_lock :: proc {
|
try_lock :: proc {
|
||||||
spinlock_try_lock,
|
spinlock_try_lock,
|
||||||
}
|
}
|
||||||
@@ -174,14 +132,6 @@ unlock :: proc {
|
|||||||
spinlock_unlock,
|
spinlock_unlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
guard :: proc {
|
|
||||||
spinlock_guard,
|
|
||||||
}
|
|
||||||
|
|
||||||
try_guard :: proc {
|
|
||||||
spinlock_try_guard,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
// ----- Tests ------------------------
|
// ----- Tests ------------------------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -189,10 +139,10 @@ import "core:sync"
|
|||||||
import "core:testing"
|
import "core:testing"
|
||||||
import "core:thread"
|
import "core:thread"
|
||||||
|
|
||||||
// Multiple threads will each add 1.0 this many times.
|
|
||||||
// If any updates are lost due to race conditions, the final sum will be wrong.
|
|
||||||
@(test)
|
@(test)
|
||||||
test_concurrent_atomic_add_no_lost_updates :: proc(t: ^testing.T) {
|
test_concurrent_atomic_add_no_lost_updates :: proc(t: ^testing.T) {
|
||||||
|
// Multiple threads will each add 1.0 this many times.
|
||||||
|
// If any updates are lost due to race conditions, the final sum will be wrong.
|
||||||
NUM_THREADS :: 8
|
NUM_THREADS :: 8
|
||||||
ITERATIONS_PER_THREAD :: 10_000
|
ITERATIONS_PER_THREAD :: 10_000
|
||||||
|
|
||||||
@@ -234,10 +184,10 @@ test_concurrent_atomic_add_no_lost_updates :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, shared_value, expected)
|
testing.expect_value(t, shared_value, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start with a known value, multiple threads subtract.
|
|
||||||
// If any updates are lost due to race conditions, the final result will be wrong.
|
|
||||||
@(test)
|
@(test)
|
||||||
test_concurrent_atomic_sub_no_lost_updates :: proc(t: ^testing.T) {
|
test_concurrent_atomic_sub_no_lost_updates :: proc(t: ^testing.T) {
|
||||||
|
// Start with a known value, multiple threads subtract.
|
||||||
|
// If any updates are lost due to race conditions, the final result will be wrong.
|
||||||
NUM_THREADS :: 8
|
NUM_THREADS :: 8
|
||||||
ITERATIONS_PER_THREAD :: 10_000
|
ITERATIONS_PER_THREAD :: 10_000
|
||||||
|
|
||||||
@@ -278,11 +228,11 @@ test_concurrent_atomic_sub_no_lost_updates :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, shared_value, 0.0)
|
testing.expect_value(t, shared_value, 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each thread multiplies by 2.0 then divides by 2.0.
|
|
||||||
// Since these are inverses, the final value should equal the starting value
|
|
||||||
// regardless of how operations interleave.
|
|
||||||
@(test)
|
@(test)
|
||||||
test_concurrent_atomic_mul_div_round_trip :: proc(t: ^testing.T) {
|
test_concurrent_atomic_mul_div_round_trip :: proc(t: ^testing.T) {
|
||||||
|
// Each thread multiplies by 2.0 then divides by 2.0.
|
||||||
|
// Since these are inverses, the final value should equal the starting value
|
||||||
|
// regardless of how operations interleave.
|
||||||
NUM_THREADS :: 8
|
NUM_THREADS :: 8
|
||||||
ITERATIONS_PER_THREAD :: 10_000
|
ITERATIONS_PER_THREAD :: 10_000
|
||||||
|
|
||||||
@@ -324,10 +274,10 @@ test_concurrent_atomic_mul_div_round_trip :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, shared_value, 1000.0)
|
testing.expect_value(t, shared_value, 1000.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the f32 type dispatch works correctly under contention.
|
|
||||||
// Same approach as the f64 add test but with f32.
|
|
||||||
@(test)
|
@(test)
|
||||||
test_atomic_add_with_f32 :: proc(t: ^testing.T) {
|
test_atomic_add_with_f32 :: proc(t: ^testing.T) {
|
||||||
|
// Verify the f32 type dispatch works correctly under contention.
|
||||||
|
// Same approach as the f64 add test but with f32.
|
||||||
NUM_THREADS :: 8
|
NUM_THREADS :: 8
|
||||||
ITERATIONS_PER_THREAD :: 10_000
|
ITERATIONS_PER_THREAD :: 10_000
|
||||||
|
|
||||||
@@ -369,17 +319,17 @@ test_atomic_add_with_f32 :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, shared_value, expected)
|
testing.expect_value(t, shared_value, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that the memory order passed to atomic_float_op's CAS success condition
|
|
||||||
// provides full ordering guarantees for the entire float operation.
|
|
||||||
//
|
|
||||||
// Both sides use atomic_add_float (not raw intrinsics) to verify:
|
|
||||||
// - Release on CAS success publishes prior non-atomic writes
|
|
||||||
// - Acquire on CAS success makes those writes visible to the reader
|
|
||||||
//
|
|
||||||
// NOTE: This test may pass even with Relaxed ordering on x86 due to its strong memory model.
|
|
||||||
// On ARM or other weak-memory architectures, using Relaxed here would likely cause failures.
|
|
||||||
@(test)
|
@(test)
|
||||||
test_atomic_release_acquire_publish_visibility :: proc(t: ^testing.T) {
|
test_atomic_release_acquire_publish_visibility :: proc(t: ^testing.T) {
|
||||||
|
// Tests that the memory order passed to atomic_float_op's CAS success condition
|
||||||
|
// provides full ordering guarantees for the entire float operation.
|
||||||
|
//
|
||||||
|
// Both sides use atomic_add_float (not raw intrinsics) to verify:
|
||||||
|
// - Release on CAS success publishes prior non-atomic writes
|
||||||
|
// - Acquire on CAS success makes those writes visible to the reader
|
||||||
|
//
|
||||||
|
// NOTE: This test may pass even with Relaxed ordering on x86 due to its strong memory model.
|
||||||
|
// On ARM or other weak-memory architectures, using Relaxed here would likely cause failures.
|
||||||
NUM_READERS :: 4
|
NUM_READERS :: 4
|
||||||
|
|
||||||
Shared_State :: struct {
|
Shared_State :: struct {
|
||||||
@@ -476,20 +426,17 @@ test_atomic_release_acquire_publish_visibility :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stress test for every spinlock acquisition variant: N threads contend on a
|
|
||||||
// single lock and perform a deliberate non-atomic read-modify-write on shared
|
|
||||||
// data. Each iteration rotates through spinlock_try_lock, spinlock_lock,
|
|
||||||
// spinlock_guard, and spinlock_try_guard so every variant runs concurrently and
|
|
||||||
// must uphold mutual exclusion on the same lock.
|
|
||||||
//
|
|
||||||
// If mutual exclusion holds:
|
|
||||||
// - `counter` ends at exactly NUM_THREADS * ITERATIONS_PER_THREAD
|
|
||||||
// - `concurrent_holders` never exceeds 1
|
|
||||||
//
|
|
||||||
// A multi-step RMW (read → relax → write) widens the critical section so
|
|
||||||
// any failure to exclude is virtually guaranteed to corrupt the counter.
|
|
||||||
@(test)
|
@(test)
|
||||||
test_spinlock_mutual_exclusion :: proc(t: ^testing.T) {
|
test_spinlock_try_lock_mutual_exclusion :: proc(t: ^testing.T) {
|
||||||
|
// Stress test for spinlock_try_lock: N threads spin-acquire the lock and
|
||||||
|
// perform a deliberate non-atomic read-modify-write on shared data.
|
||||||
|
//
|
||||||
|
// If mutual exclusion holds:
|
||||||
|
// - `counter` ends at exactly NUM_THREADS * ITERATIONS_PER_THREAD
|
||||||
|
// - `concurrent_holders` never exceeds 1
|
||||||
|
//
|
||||||
|
// A multi-step RMW (read → relax → write) widens the critical section so
|
||||||
|
// any failure to exclude is virtually guaranteed to corrupt the counter.
|
||||||
NUM_THREADS :: 8
|
NUM_THREADS :: 8
|
||||||
ITERATIONS_PER_THREAD :: 50_000
|
ITERATIONS_PER_THREAD :: 50_000
|
||||||
|
|
||||||
@@ -514,9 +461,21 @@ test_spinlock_mutual_exclusion :: proc(t: ^testing.T) {
|
|||||||
barrier: sync.Barrier
|
barrier: sync.Barrier
|
||||||
sync.barrier_init(&barrier, NUM_THREADS)
|
sync.barrier_init(&barrier, NUM_THREADS)
|
||||||
|
|
||||||
// The single critical section every acquisition variant must protect. Sharing
|
thread_proc :: proc(th: ^thread.Thread) {
|
||||||
// it guarantees they all stress the exact same non-atomic read-modify-write.
|
ctx := cast(^Thread_Data)th.data
|
||||||
critical_section :: proc(s: ^Shared) {
|
s := ctx.shared
|
||||||
|
|
||||||
|
// All threads rendezvous here for maximum contention.
|
||||||
|
sync.barrier_wait(ctx.barrier)
|
||||||
|
|
||||||
|
for _ in 0 ..< ITERATIONS_PER_THREAD {
|
||||||
|
// Spin on try_lock until we acquire it.
|
||||||
|
for !spinlock_try_lock(&s.lock) {
|
||||||
|
intrinsics.cpu_relax()
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- critical section start ---
|
||||||
|
|
||||||
// Atomically bump the holder count so we can detect overlapping holders.
|
// Atomically bump the holder count so we can detect overlapping holders.
|
||||||
holders := intrinsics.atomic_add_explicit(&s.concurrent_holders, 1, .Relaxed)
|
holders := intrinsics.atomic_add_explicit(&s.concurrent_holders, 1, .Relaxed)
|
||||||
|
|
||||||
@@ -535,44 +494,10 @@ test_spinlock_mutual_exclusion :: proc(t: ^testing.T) {
|
|||||||
s.counter = val + 1
|
s.counter = val + 1
|
||||||
|
|
||||||
intrinsics.atomic_sub_explicit(&s.concurrent_holders, 1, .Relaxed)
|
intrinsics.atomic_sub_explicit(&s.concurrent_holders, 1, .Relaxed)
|
||||||
}
|
|
||||||
|
|
||||||
thread_proc :: proc(th: ^thread.Thread) {
|
// --- critical section end ---
|
||||||
ctx := cast(^Thread_Data)th.data
|
|
||||||
s := ctx.shared
|
|
||||||
|
|
||||||
// All threads rendezvous here for maximum contention.
|
|
||||||
sync.barrier_wait(ctx.barrier)
|
|
||||||
|
|
||||||
for i in 0 ..< ITERATIONS_PER_THREAD {
|
|
||||||
// Rotate through every acquisition variant so they all contend on the
|
|
||||||
// same lock simultaneously and must each uphold mutual exclusion.
|
|
||||||
switch i & 3 {
|
|
||||||
case 0:
|
|
||||||
// Manual spin on try_lock until we acquire it.
|
|
||||||
for !spinlock_try_lock(&s.lock) {
|
|
||||||
intrinsics.cpu_relax()
|
|
||||||
}
|
|
||||||
critical_section(s)
|
|
||||||
spinlock_unlock(&s.lock)
|
spinlock_unlock(&s.lock)
|
||||||
case 1:
|
|
||||||
// Blocking lock that loops internally until acquired.
|
|
||||||
spinlock_lock(&s.lock)
|
|
||||||
critical_section(s)
|
|
||||||
spinlock_unlock(&s.lock)
|
|
||||||
case 2: // Scoped guard: unlocks automatically at the end of the block.
|
|
||||||
if spinlock_guard(&s.lock) {
|
|
||||||
critical_section(s)
|
|
||||||
}
|
|
||||||
case 3: // Scoped try-guard: retry until acquired, auto-unlocks on success.
|
|
||||||
for {
|
|
||||||
if spinlock_try_guard(&s.lock) {
|
|
||||||
critical_section(s)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
intrinsics.cpu_relax()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,774 +0,0 @@
|
|||||||
package quantity
|
|
||||||
|
|
||||||
import "base:intrinsics"
|
|
||||||
|
|
||||||
GRAMS_PER_POUND :: 453.59237
|
|
||||||
OUNCES_PER_POUND :: 16
|
|
||||||
GRAMS_PER_OUNCE :: GRAMS_PER_POUND / OUNCES_PER_POUND
|
|
||||||
|
|
||||||
KILO_GRAMS_PER_POUND :: GRAMS_PER_POUND / KILO
|
|
||||||
MILLI_GRAMS_PER_POUND :: GRAMS_PER_POUND * MILLI
|
|
||||||
MICRO_GRAMS_PER_POUND :: GRAMS_PER_POUND * MICRO
|
|
||||||
NANO_GRAMS_PER_POUND :: GRAMS_PER_POUND * NANO
|
|
||||||
|
|
||||||
KILO_GRAMS_PER_OUNCE :: GRAMS_PER_OUNCE / KILO
|
|
||||||
MILLI_GRAMS_PER_OUNCE :: GRAMS_PER_OUNCE * MILLI
|
|
||||||
MICRO_GRAMS_PER_OUNCE :: GRAMS_PER_OUNCE * MICRO
|
|
||||||
NANO_GRAMS_PER_OUNCE :: GRAMS_PER_OUNCE * NANO
|
|
||||||
|
|
||||||
//----- Grams ----------------------------------
|
|
||||||
Grams :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_kilo_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
grams_to_kilo_grams :: #force_inline proc "contextless" (
|
|
||||||
grams: Grams($V),
|
|
||||||
) -> Kilo_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Kilo_Grams(V){grams.v / KILO}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_milli_grams procedure group.
|
|
||||||
grams_to_milli_grams :: #force_inline proc "contextless" (
|
|
||||||
grams: Grams($V),
|
|
||||||
) -> Milli_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Milli_Grams(V){grams.v * MILLI}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_grams procedure group.
|
|
||||||
grams_to_micro_grams :: #force_inline proc "contextless" (
|
|
||||||
grams: Grams($V),
|
|
||||||
) -> Micro_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Micro_Grams(V){grams.v * MICRO}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_nano_grams procedure group.
|
|
||||||
grams_to_nano_grams :: #force_inline proc "contextless" (
|
|
||||||
grams: Grams($V),
|
|
||||||
) -> Nano_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Nano_Grams(V){grams.v * NANO}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_pounds procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
grams_to_pounds :: #force_inline proc "contextless" (
|
|
||||||
grams: Grams($V),
|
|
||||||
) -> Pounds(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Pounds(V){grams.v / GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_ounces procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
grams_to_ounces :: #force_inline proc "contextless" (
|
|
||||||
grams: Grams($V),
|
|
||||||
) -> Ounces(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Ounces(V){grams.v / GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Kilograms ----------------------------------
|
|
||||||
Kilo_Grams :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_grams procedure group.
|
|
||||||
kilo_grams_to_grams :: #force_inline proc "contextless" (
|
|
||||||
kilo_grams: Kilo_Grams($V),
|
|
||||||
) -> Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Grams(V){kilo_grams.v * KILO}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_milli_grams procedure group.
|
|
||||||
kilo_grams_to_milli_grams :: #force_inline proc "contextless" (
|
|
||||||
kilo_grams: Kilo_Grams($V),
|
|
||||||
) -> Milli_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Milli_Grams(V){kilo_grams.v * (KILO * MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_grams procedure group.
|
|
||||||
kilo_grams_to_micro_grams :: #force_inline proc "contextless" (
|
|
||||||
kilo_grams: Kilo_Grams($V),
|
|
||||||
) -> Micro_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Micro_Grams(V){kilo_grams.v * (KILO * MICRO)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_nano_grams procedure group.
|
|
||||||
kilo_grams_to_nano_grams :: #force_inline proc "contextless" (
|
|
||||||
kilo_grams: Kilo_Grams($V),
|
|
||||||
) -> Nano_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Nano_Grams(V){kilo_grams.v * (KILO * NANO)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_pounds procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
kilo_grams_to_pounds :: #force_inline proc "contextless" (
|
|
||||||
kilo_grams: Kilo_Grams($V),
|
|
||||||
) -> Pounds(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Pounds(V){kilo_grams.v / KILO_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_ounces procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
kilo_grams_to_ounces :: #force_inline proc "contextless" (
|
|
||||||
kilo_grams: Kilo_Grams($V),
|
|
||||||
) -> Ounces(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Ounces(V){kilo_grams.v / KILO_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Milligrams ----------------------------------
|
|
||||||
Milli_Grams :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
milli_grams_to_grams :: #force_inline proc "contextless" (
|
|
||||||
milli_grams: Milli_Grams($V),
|
|
||||||
) -> Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Grams(V){milli_grams.v / MILLI}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_kilo_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
milli_grams_to_kilo_grams :: #force_inline proc "contextless" (
|
|
||||||
milli_grams: Milli_Grams($V),
|
|
||||||
) -> Kilo_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Kilo_Grams(V){milli_grams.v / (KILO * MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_grams procedure group.
|
|
||||||
milli_grams_to_micro_grams :: #force_inline proc "contextless" (
|
|
||||||
milli_grams: Milli_Grams($V),
|
|
||||||
) -> Micro_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Micro_Grams(V){milli_grams.v * (MICRO / MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_nano_grams procedure group.
|
|
||||||
milli_grams_to_nano_grams :: #force_inline proc "contextless" (
|
|
||||||
milli_grams: Milli_Grams($V),
|
|
||||||
) -> Nano_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Nano_Grams(V){milli_grams.v * (NANO / MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_pounds procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
milli_grams_to_pounds :: #force_inline proc "contextless" (
|
|
||||||
milli_grams: Milli_Grams($V),
|
|
||||||
) -> Pounds(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Pounds(V){milli_grams.v / MILLI_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_ounces procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
milli_grams_to_ounces :: #force_inline proc "contextless" (
|
|
||||||
milli_grams: Milli_Grams($V),
|
|
||||||
) -> Ounces(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Ounces(V){milli_grams.v / MILLI_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Micrograms ----------------------------------
|
|
||||||
Micro_Grams :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_grams_to_grams :: #force_inline proc "contextless" (
|
|
||||||
micro_grams: Micro_Grams($V),
|
|
||||||
) -> Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Grams(V){micro_grams.v / MICRO}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_kilo_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_grams_to_kilo_grams :: #force_inline proc "contextless" (
|
|
||||||
micro_grams: Micro_Grams($V),
|
|
||||||
) -> Kilo_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Kilo_Grams(V){micro_grams.v / (KILO * MICRO)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_milli_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_grams_to_milli_grams :: #force_inline proc "contextless" (
|
|
||||||
micro_grams: Micro_Grams($V),
|
|
||||||
) -> Milli_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Milli_Grams(V){micro_grams.v / (MICRO / MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_nano_grams procedure group.
|
|
||||||
micro_grams_to_nano_grams :: #force_inline proc "contextless" (
|
|
||||||
micro_grams: Micro_Grams($V),
|
|
||||||
) -> Nano_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Nano_Grams(V){micro_grams.v * (NANO / MICRO)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_pounds procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_grams_to_pounds :: #force_inline proc "contextless" (
|
|
||||||
micro_grams: Micro_Grams($V),
|
|
||||||
) -> Pounds(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Pounds(V){micro_grams.v / MICRO_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_ounces procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_grams_to_ounces :: #force_inline proc "contextless" (
|
|
||||||
micro_grams: Micro_Grams($V),
|
|
||||||
) -> Ounces(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Ounces(V){micro_grams.v / MICRO_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Nanograms ----------------------------------
|
|
||||||
Nano_Grams :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
nano_grams_to_grams :: #force_inline proc "contextless" (
|
|
||||||
nano_grams: Nano_Grams($V),
|
|
||||||
) -> Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Grams(V){nano_grams.v / NANO}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_kilo_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
nano_grams_to_kilo_grams :: #force_inline proc "contextless" (
|
|
||||||
nano_grams: Nano_Grams($V),
|
|
||||||
) -> Kilo_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Kilo_Grams(V){nano_grams.v / (KILO * NANO)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_milli_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
nano_grams_to_milli_grams :: #force_inline proc "contextless" (
|
|
||||||
nano_grams: Nano_Grams($V),
|
|
||||||
) -> Milli_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Milli_Grams(V){nano_grams.v / (NANO / MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_grams procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
nano_grams_to_micro_grams :: #force_inline proc "contextless" (
|
|
||||||
nano_grams: Nano_Grams($V),
|
|
||||||
) -> Micro_Grams(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Micro_Grams(V){nano_grams.v / (NANO / MICRO)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_pounds procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
nano_grams_to_pounds :: #force_inline proc "contextless" (
|
|
||||||
nano_grams: Nano_Grams($V),
|
|
||||||
) -> Pounds(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Pounds(V){nano_grams.v / NANO_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_ounces procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
nano_grams_to_ounces :: #force_inline proc "contextless" (
|
|
||||||
nano_grams: Nano_Grams($V),
|
|
||||||
) -> Ounces(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Ounces(V){nano_grams.v / NANO_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Pounds ----------------------------------
|
|
||||||
Pounds :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_grams procedure group.
|
|
||||||
pounds_to_grams :: #force_inline proc "contextless" (
|
|
||||||
pounds: Pounds($V),
|
|
||||||
) -> Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Grams(V){pounds.v * GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_kilo_grams procedure group.
|
|
||||||
pounds_to_kilo_grams :: #force_inline proc "contextless" (
|
|
||||||
pounds: Pounds($V),
|
|
||||||
) -> Kilo_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Kilo_Grams(V){pounds.v * KILO_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_milli_grams procedure group.
|
|
||||||
pounds_to_milli_grams :: #force_inline proc "contextless" (
|
|
||||||
pounds: Pounds($V),
|
|
||||||
) -> Milli_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Milli_Grams(V){pounds.v * MILLI_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_grams procedure group.
|
|
||||||
pounds_to_micro_grams :: #force_inline proc "contextless" (
|
|
||||||
pounds: Pounds($V),
|
|
||||||
) -> Micro_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Micro_Grams(V){pounds.v * MICRO_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_nano_grams procedure group.
|
|
||||||
pounds_to_nano_grams :: #force_inline proc "contextless" (
|
|
||||||
pounds: Pounds($V),
|
|
||||||
) -> Nano_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Nano_Grams(V){pounds.v * NANO_GRAMS_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_ounces procedure group.
|
|
||||||
pounds_to_ounces :: #force_inline proc "contextless" (
|
|
||||||
pounds: Pounds($V),
|
|
||||||
) -> Ounces(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Ounces(V){pounds.v * OUNCES_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Ounces ----------------------------------
|
|
||||||
Ounces :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_grams procedure group.
|
|
||||||
ounces_to_grams :: #force_inline proc "contextless" (
|
|
||||||
ounces: Ounces($V),
|
|
||||||
) -> Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Grams(V){ounces.v * GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_kilo_grams procedure group.
|
|
||||||
ounces_to_kilo_grams :: #force_inline proc "contextless" (
|
|
||||||
ounces: Ounces($V),
|
|
||||||
) -> Kilo_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Kilo_Grams(V){ounces.v * KILO_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_milli_grams procedure group.
|
|
||||||
ounces_to_milli_grams :: #force_inline proc "contextless" (
|
|
||||||
ounces: Ounces($V),
|
|
||||||
) -> Milli_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Milli_Grams(V){ounces.v * MILLI_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_grams procedure group.
|
|
||||||
ounces_to_micro_grams :: #force_inline proc "contextless" (
|
|
||||||
ounces: Ounces($V),
|
|
||||||
) -> Micro_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Micro_Grams(V){ounces.v * MICRO_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_nano_grams procedure group.
|
|
||||||
ounces_to_nano_grams :: #force_inline proc "contextless" (
|
|
||||||
ounces: Ounces($V),
|
|
||||||
) -> Nano_Grams(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Nano_Grams(V){ounces.v * NANO_GRAMS_PER_OUNCE}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_pounds procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
ounces_to_pounds :: #force_inline proc "contextless" (
|
|
||||||
ounces: Ounces($V),
|
|
||||||
) -> Pounds(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Pounds(V){ounces.v / OUNCES_PER_POUND}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----- Conversion Overloads ------------------------
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
to_grams :: proc {
|
|
||||||
kilo_grams_to_grams,
|
|
||||||
milli_grams_to_grams,
|
|
||||||
micro_grams_to_grams,
|
|
||||||
nano_grams_to_grams,
|
|
||||||
pounds_to_grams,
|
|
||||||
ounces_to_grams,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_kilo_grams :: proc {
|
|
||||||
grams_to_kilo_grams,
|
|
||||||
milli_grams_to_kilo_grams,
|
|
||||||
micro_grams_to_kilo_grams,
|
|
||||||
nano_grams_to_kilo_grams,
|
|
||||||
pounds_to_kilo_grams,
|
|
||||||
ounces_to_kilo_grams,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_milli_grams :: proc {
|
|
||||||
grams_to_milli_grams,
|
|
||||||
kilo_grams_to_milli_grams,
|
|
||||||
micro_grams_to_milli_grams,
|
|
||||||
nano_grams_to_milli_grams,
|
|
||||||
pounds_to_milli_grams,
|
|
||||||
ounces_to_milli_grams,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_micro_grams :: proc {
|
|
||||||
grams_to_micro_grams,
|
|
||||||
kilo_grams_to_micro_grams,
|
|
||||||
milli_grams_to_micro_grams,
|
|
||||||
nano_grams_to_micro_grams,
|
|
||||||
pounds_to_micro_grams,
|
|
||||||
ounces_to_micro_grams,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_nano_grams :: proc {
|
|
||||||
grams_to_nano_grams,
|
|
||||||
kilo_grams_to_nano_grams,
|
|
||||||
milli_grams_to_nano_grams,
|
|
||||||
micro_grams_to_nano_grams,
|
|
||||||
pounds_to_nano_grams,
|
|
||||||
ounces_to_nano_grams,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_pounds :: proc {
|
|
||||||
grams_to_pounds,
|
|
||||||
kilo_grams_to_pounds,
|
|
||||||
milli_grams_to_pounds,
|
|
||||||
micro_grams_to_pounds,
|
|
||||||
nano_grams_to_pounds,
|
|
||||||
ounces_to_pounds,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_ounces :: proc {
|
|
||||||
grams_to_ounces,
|
|
||||||
kilo_grams_to_ounces,
|
|
||||||
milli_grams_to_ounces,
|
|
||||||
micro_grams_to_ounces,
|
|
||||||
nano_grams_to_ounces,
|
|
||||||
pounds_to_ounces,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----- Tests ------------------------
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
import "core:testing"
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_grams_to_kilo_grams :: proc(t: ^testing.T) {
|
|
||||||
grams := Grams(int){12_000}
|
|
||||||
kilo_grams := to_kilo_grams(grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, kilo_grams, Kilo_Grams(int){12})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_grams_to_milli_grams :: proc(t: ^testing.T) {
|
|
||||||
grams := Grams(int){12}
|
|
||||||
milli_grams := to_milli_grams(grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, milli_grams, Milli_Grams(int){12_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_grams_to_micro_grams :: proc(t: ^testing.T) {
|
|
||||||
grams := Grams(int){12}
|
|
||||||
micro_grams := to_micro_grams(grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, micro_grams, Micro_Grams(int){12_000_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_grams_to_nano_grams :: proc(t: ^testing.T) {
|
|
||||||
grams := Grams(int){12}
|
|
||||||
nano_grams := to_nano_grams(grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, nano_grams, Nano_Grams(int){12_000_000_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_grams_to_pounds :: proc(t: ^testing.T) {
|
|
||||||
grams := Grams(f32){453.59237}
|
|
||||||
pounds := to_pounds(grams)
|
|
||||||
|
|
||||||
testing.expect(t, pounds.v > 0.99 && pounds.v < 1.01)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_grams_to_ounces :: proc(t: ^testing.T) {
|
|
||||||
grams := Grams(f32){28.349523125}
|
|
||||||
ounces := to_ounces(grams)
|
|
||||||
|
|
||||||
testing.expect(t, ounces.v > 0.99 && ounces.v < 1.01)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_kilo_grams_to_grams :: proc(t: ^testing.T) {
|
|
||||||
kilo_grams := Kilo_Grams(int){12}
|
|
||||||
grams := to_grams(kilo_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, grams, Grams(int){12_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_kilo_grams_to_milli_grams :: proc(t: ^testing.T) {
|
|
||||||
kilo_grams := Kilo_Grams(int){5}
|
|
||||||
milli_grams := to_milli_grams(kilo_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, milli_grams, Milli_Grams(int){5_000_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_kilo_grams_to_micro_grams :: proc(t: ^testing.T) {
|
|
||||||
kilo_grams := Kilo_Grams(int){5}
|
|
||||||
micro_grams := to_micro_grams(kilo_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, micro_grams, Micro_Grams(int){5_000_000_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_kilo_grams_to_nano_grams :: proc(t: ^testing.T) {
|
|
||||||
kilo_grams := Kilo_Grams(int){5}
|
|
||||||
nano_grams := to_nano_grams(kilo_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, nano_grams, Nano_Grams(int){5_000_000_000_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_kilo_grams_to_pounds :: proc(t: ^testing.T) {
|
|
||||||
kilo_grams := Kilo_Grams(f32){0.45359237}
|
|
||||||
pounds := to_pounds(kilo_grams)
|
|
||||||
|
|
||||||
testing.expect(t, pounds.v > 0.99 && pounds.v < 1.01)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_kilo_grams_to_ounces :: proc(t: ^testing.T) {
|
|
||||||
kilo_grams := Kilo_Grams(f32){0.028349523125}
|
|
||||||
ounces := to_ounces(kilo_grams)
|
|
||||||
|
|
||||||
testing.expect(t, ounces.v > 0.99 && ounces.v < 1.01)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_milli_grams_to_grams :: proc(t: ^testing.T) {
|
|
||||||
milli_grams := Milli_Grams(int){12_000}
|
|
||||||
grams := to_grams(milli_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, grams, Grams(int){12})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_milli_grams_to_kilo_grams :: proc(t: ^testing.T) {
|
|
||||||
milli_grams := Milli_Grams(int){5_000_000}
|
|
||||||
kilo_grams := to_kilo_grams(milli_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, kilo_grams, Kilo_Grams(int){5})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_milli_grams_to_micro_grams :: proc(t: ^testing.T) {
|
|
||||||
milli_grams := Milli_Grams(int){5}
|
|
||||||
micro_grams := to_micro_grams(milli_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, micro_grams, Micro_Grams(int){5_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_milli_grams_to_nano_grams :: proc(t: ^testing.T) {
|
|
||||||
milli_grams := Milli_Grams(int){5}
|
|
||||||
nano_grams := to_nano_grams(milli_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, nano_grams, Nano_Grams(int){5_000_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_milli_grams_to_pounds :: proc(t: ^testing.T) {
|
|
||||||
milli_grams := Milli_Grams(f64){453_592.37}
|
|
||||||
pounds := to_pounds(milli_grams)
|
|
||||||
|
|
||||||
testing.expect(t, pounds.v > 0.9999999 && pounds.v < 1.0000001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_milli_grams_to_ounces :: proc(t: ^testing.T) {
|
|
||||||
milli_grams := Milli_Grams(f64){28_349.523125}
|
|
||||||
ounces := to_ounces(milli_grams)
|
|
||||||
|
|
||||||
testing.expect(t, ounces.v > 0.9999999 && ounces.v < 1.0000001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_grams_to_grams :: proc(t: ^testing.T) {
|
|
||||||
micro_grams := Micro_Grams(int){12_000_000}
|
|
||||||
grams := to_grams(micro_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, grams, Grams(int){12})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_grams_to_kilo_grams :: proc(t: ^testing.T) {
|
|
||||||
micro_grams := Micro_Grams(int){5_000_000_000}
|
|
||||||
kilo_grams := to_kilo_grams(micro_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, kilo_grams, Kilo_Grams(int){5})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_grams_to_milli_grams :: proc(t: ^testing.T) {
|
|
||||||
micro_grams := Micro_Grams(int){5_000}
|
|
||||||
milli_grams := to_milli_grams(micro_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, milli_grams, Milli_Grams(int){5})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_grams_to_nano_grams :: proc(t: ^testing.T) {
|
|
||||||
micro_grams := Micro_Grams(int){5}
|
|
||||||
nano_grams := to_nano_grams(micro_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, nano_grams, Nano_Grams(int){5_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_grams_to_pounds :: proc(t: ^testing.T) {
|
|
||||||
micro_grams := Micro_Grams(f64){453_592_370}
|
|
||||||
pounds := to_pounds(micro_grams)
|
|
||||||
|
|
||||||
testing.expect(t, pounds.v > 0.9999999 && pounds.v < 1.0000001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_grams_to_ounces :: proc(t: ^testing.T) {
|
|
||||||
micro_grams := Micro_Grams(f64){28_349_523.125}
|
|
||||||
ounces := to_ounces(micro_grams)
|
|
||||||
|
|
||||||
testing.expect(t, ounces.v > 0.9999999 && ounces.v < 1.0000001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_nano_grams_to_grams :: proc(t: ^testing.T) {
|
|
||||||
nano_grams := Nano_Grams(int){12_000_000_000}
|
|
||||||
grams := to_grams(nano_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, grams, Grams(int){12})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_nano_grams_to_kilo_grams :: proc(t: ^testing.T) {
|
|
||||||
nano_grams := Nano_Grams(int){5_000_000_000_000}
|
|
||||||
kilo_grams := to_kilo_grams(nano_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, kilo_grams, Kilo_Grams(int){5})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_nano_grams_to_milli_grams :: proc(t: ^testing.T) {
|
|
||||||
nano_grams := Nano_Grams(int){5_000_000}
|
|
||||||
milli_grams := to_milli_grams(nano_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, milli_grams, Milli_Grams(int){5})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_nano_grams_to_micro_grams :: proc(t: ^testing.T) {
|
|
||||||
nano_grams := Nano_Grams(int){5_000}
|
|
||||||
micro_grams := to_micro_grams(nano_grams)
|
|
||||||
|
|
||||||
testing.expect_value(t, micro_grams, Micro_Grams(int){5})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_nano_grams_to_pounds :: proc(t: ^testing.T) {
|
|
||||||
nano_grams := Nano_Grams(f64){453_592_370_000}
|
|
||||||
pounds := to_pounds(nano_grams)
|
|
||||||
|
|
||||||
testing.expect(t, pounds.v > 0.9999999 && pounds.v < 1.0000001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_nano_grams_to_ounces :: proc(t: ^testing.T) {
|
|
||||||
nano_grams := Nano_Grams(f64){28_349_523_125}
|
|
||||||
ounces := to_ounces(nano_grams)
|
|
||||||
|
|
||||||
testing.expect(t, ounces.v > 0.9999999 && ounces.v < 1.0000001)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_pounds_to_grams :: proc(t: ^testing.T) {
|
|
||||||
pounds := Pounds(f32){1}
|
|
||||||
grams := to_grams(pounds)
|
|
||||||
|
|
||||||
testing.expect(t, grams.v > 453.59 && grams.v < 453.6)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_pounds_to_kilo_grams :: proc(t: ^testing.T) {
|
|
||||||
pounds := Pounds(f32){1}
|
|
||||||
kilo_grams := to_kilo_grams(pounds)
|
|
||||||
|
|
||||||
testing.expect(t, kilo_grams.v > 0.4535 && kilo_grams.v < 0.4536)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_pounds_to_milli_grams :: proc(t: ^testing.T) {
|
|
||||||
pounds := Pounds(f64){1}
|
|
||||||
milli_grams := to_milli_grams(pounds)
|
|
||||||
|
|
||||||
testing.expect(t, milli_grams.v > 453_592.369 && milli_grams.v < 453_592.371)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_pounds_to_micro_grams :: proc(t: ^testing.T) {
|
|
||||||
pounds := Pounds(f64){1}
|
|
||||||
micro_grams := to_micro_grams(pounds)
|
|
||||||
|
|
||||||
testing.expect(t, micro_grams.v > 453_592_369.9 && micro_grams.v < 453_592_370.1)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_pounds_to_nano_grams :: proc(t: ^testing.T) {
|
|
||||||
pounds := Pounds(f64){1}
|
|
||||||
nano_grams := to_nano_grams(pounds)
|
|
||||||
|
|
||||||
testing.expect(t, nano_grams.v > 453_592_369_999.0 && nano_grams.v < 453_592_370_001.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_pounds_to_ounces :: proc(t: ^testing.T) {
|
|
||||||
pounds := Pounds(int){2}
|
|
||||||
ounces := to_ounces(pounds)
|
|
||||||
|
|
||||||
testing.expect_value(t, ounces, Ounces(int){32})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_ounces_to_grams :: proc(t: ^testing.T) {
|
|
||||||
ounces := Ounces(f32){1}
|
|
||||||
grams := to_grams(ounces)
|
|
||||||
|
|
||||||
testing.expect(t, grams.v > 28.34 && grams.v < 28.35)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_ounces_to_kilo_grams :: proc(t: ^testing.T) {
|
|
||||||
ounces := Ounces(f32){1}
|
|
||||||
kilo_grams := to_kilo_grams(ounces)
|
|
||||||
|
|
||||||
testing.expect(t, kilo_grams.v > 0.0283 && kilo_grams.v < 0.0284)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_ounces_to_milli_grams :: proc(t: ^testing.T) {
|
|
||||||
ounces := Ounces(f64){1}
|
|
||||||
milli_grams := to_milli_grams(ounces)
|
|
||||||
|
|
||||||
testing.expect(t, milli_grams.v > 28_349.523 && milli_grams.v < 28_349.524)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_ounces_to_micro_grams :: proc(t: ^testing.T) {
|
|
||||||
ounces := Ounces(f64){1}
|
|
||||||
micro_grams := to_micro_grams(ounces)
|
|
||||||
|
|
||||||
testing.expect(t, micro_grams.v > 28_349_523.124 && micro_grams.v < 28_349_523.126)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_ounces_to_nano_grams :: proc(t: ^testing.T) {
|
|
||||||
ounces := Ounces(f64){1}
|
|
||||||
nano_grams := to_nano_grams(ounces)
|
|
||||||
|
|
||||||
testing.expect(t, nano_grams.v > 28_349_523_124.0 && nano_grams.v < 28_349_523_126.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_ounces_to_pounds :: proc(t: ^testing.T) {
|
|
||||||
ounces := Ounces(int){32}
|
|
||||||
pounds := to_pounds(ounces)
|
|
||||||
|
|
||||||
testing.expect_value(t, pounds, Pounds(int){2})
|
|
||||||
}
|
|
||||||
@@ -10,14 +10,14 @@ Pascals :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_kilo_pascals procedure group.
|
@(private = "file")
|
||||||
pascals_to_kilo_pascals :: #force_inline proc "contextless" (
|
pascals_to_kilo_pascals :: #force_inline proc "contextless" (
|
||||||
pascals: Pascals($V),
|
pascals: Pascals($V),
|
||||||
) -> Kilo_Pascals(V) where intrinsics.type_is_numeric(V) {
|
) -> Kilo_Pascals(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Kilo_Pascals(V){pascals.v / KILO}
|
return Kilo_Pascals(V){pascals.v / KILO}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_torr procedure group.
|
@(private = "file")
|
||||||
pascals_to_torr :: #force_inline proc "contextless" (
|
pascals_to_torr :: #force_inline proc "contextless" (
|
||||||
pascals: Pascals($V),
|
pascals: Pascals($V),
|
||||||
) -> Torr(V) where intrinsics.type_is_float(V) {
|
) -> Torr(V) where intrinsics.type_is_float(V) {
|
||||||
@@ -29,14 +29,13 @@ Kilo_Pascals :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_pascals procedure group.
|
@(private = "file")
|
||||||
kilo_pascals_to_pascals :: #force_inline proc "contextless" (
|
kilo_pascals_to_pascals :: #force_inline proc "contextless" (
|
||||||
kilo_pascals: Kilo_Pascals($V),
|
kilo_pascals: Kilo_Pascals($V),
|
||||||
) -> Pascals(V) where intrinsics.type_is_numeric(V) {
|
) -> Pascals(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Pascals(V){kilo_pascals.v * KILO}
|
return Pascals(V){kilo_pascals.v * KILO}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_psi procedure group.
|
|
||||||
kilo_pascals_to_psi :: #force_inline proc "contextless" (
|
kilo_pascals_to_psi :: #force_inline proc "contextless" (
|
||||||
kilo_pascals: Kilo_Pascals($V),
|
kilo_pascals: Kilo_Pascals($V),
|
||||||
) -> Psi(V) where intrinsics.type_is_float(V) {
|
) -> Psi(V) where intrinsics.type_is_float(V) {
|
||||||
@@ -48,7 +47,7 @@ Torr :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_pascals procedure group.
|
@(private = "file")
|
||||||
torr_to_pascals :: #force_inline proc "contextless" (
|
torr_to_pascals :: #force_inline proc "contextless" (
|
||||||
torr: Torr($V),
|
torr: Torr($V),
|
||||||
) -> Pascals(V) where intrinsics.type_is_float(V) {
|
) -> Pascals(V) where intrinsics.type_is_float(V) {
|
||||||
@@ -60,7 +59,6 @@ Psi :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_kilo_pascals procedure group.
|
|
||||||
psi_to_kilo_pascals :: #force_inline proc "contextless" (
|
psi_to_kilo_pascals :: #force_inline proc "contextless" (
|
||||||
psi: Psi($V),
|
psi: Psi($V),
|
||||||
) -> Kilo_Pascals(V) where intrinsics.type_is_float(V) {
|
) -> Kilo_Pascals(V) where intrinsics.type_is_float(V) {
|
||||||
|
|||||||
+6
-108
@@ -17,11 +17,6 @@ kelvins_celsius_offset :: #force_inline proc "contextless" (
|
|||||||
return OFFSET
|
return OFFSET
|
||||||
}
|
}
|
||||||
|
|
||||||
@(private = "file")
|
|
||||||
FAHRENHEIT_PER_CELSIUS_DEGREE :: 9.0 / 5.0
|
|
||||||
@(private = "file")
|
|
||||||
CELSIUS_PER_FAHRENHEIT_DEGREE :: 5.0 / 9.0
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
// ----- Types ------------------------
|
// ----- Types ------------------------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -30,33 +25,26 @@ Kelvins :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_celsius procedure group.
|
@(private = "file")
|
||||||
kelvins_to_celsius :: #force_inline proc "contextless" (
|
kelvins_to_celsius :: #force_inline proc "contextless" (
|
||||||
kelvins: Kelvins($V),
|
kelvins: Kelvins($V),
|
||||||
) -> Celsius(V) where intrinsics.type_is_numeric(V) {
|
) -> Celsius(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Celsius(V){kelvins.v - kelvins_celsius_offset(V)}
|
return Celsius(V){kelvins.v - kelvins_celsius_offset(V)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_deci_kelvins procedure group.
|
@(private = "file")
|
||||||
kelvins_to_deci_kelvins :: #force_inline proc "contextless" (
|
kelvins_to_deci_kelvins :: #force_inline proc "contextless" (
|
||||||
kelvins: Kelvins($V),
|
kelvins: Kelvins($V),
|
||||||
) -> Deci_Kelvins(V) where intrinsics.type_is_numeric(V) {
|
) -> Deci_Kelvins(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Deci_Kelvins(V){kelvins.v * DECI}
|
return Deci_Kelvins(V){kelvins.v * DECI}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_fahrenheit procedure group.
|
|
||||||
kelvins_to_fahrenheit :: #force_inline proc "contextless" (
|
|
||||||
kelvins: Kelvins($V),
|
|
||||||
) -> Fahrenheit(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return celsius_to_fahrenheit(kelvins_to_celsius(kelvins))
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Decikelvins ----------------------------------
|
//----- Decikelvins ----------------------------------
|
||||||
Deci_Kelvins :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
Deci_Kelvins :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_kelvins procedure group.
|
@(private = "file")
|
||||||
deci_kelvins_to_kelvins :: #force_inline proc "contextless" (
|
deci_kelvins_to_kelvins :: #force_inline proc "contextless" (
|
||||||
deci_kelvins: Deci_Kelvins($V),
|
deci_kelvins: Deci_Kelvins($V),
|
||||||
) -> Kelvins(V) where intrinsics.type_is_numeric(V) {
|
) -> Kelvins(V) where intrinsics.type_is_numeric(V) {
|
||||||
@@ -68,74 +56,38 @@ Celsius :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_kelvins procedure group.
|
@(private = "file")
|
||||||
celsius_to_kelvins :: #force_inline proc "contextless" (
|
celsius_to_kelvins :: #force_inline proc "contextless" (
|
||||||
degrees_celsius: Celsius($V),
|
degrees_celsius: Celsius($V),
|
||||||
) -> Kelvins(V) where intrinsics.type_is_numeric(V) {
|
) -> Kelvins(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Kelvins(V){degrees_celsius.v + kelvins_celsius_offset(V)}
|
return Kelvins(V){degrees_celsius.v + kelvins_celsius_offset(V)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_deci_celsius procedure group.
|
@(private = "file")
|
||||||
celsius_to_deci_celsius :: #force_inline proc "contextless" (
|
celsius_to_deci_celsius :: #force_inline proc "contextless" (
|
||||||
degrees_celsius: Celsius($V),
|
degrees_celsius: Celsius($V),
|
||||||
) -> Deci_Celsius(V) where intrinsics.type_is_numeric(V) {
|
) -> Deci_Celsius(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Deci_Celsius(V){degrees_celsius.v * DECI}
|
return Deci_Celsius(V){degrees_celsius.v * DECI}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_fahrenheit procedure group.
|
|
||||||
@(fast_math = {.Allow_Contract})
|
|
||||||
celsius_to_fahrenheit :: #force_inline proc "contextless" (
|
|
||||||
degrees_celsius: Celsius($V),
|
|
||||||
) -> Fahrenheit(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
when intrinsics.type_is_float(V) {
|
|
||||||
return Fahrenheit(V){degrees_celsius.v * FAHRENHEIT_PER_CELSIUS_DEGREE + 32.0}
|
|
||||||
} else {
|
|
||||||
return Fahrenheit(V){degrees_celsius.v * 9 / 5 + 32}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Deci Degrees Celsius ----------------------------------
|
//----- Deci Degrees Celsius ----------------------------------
|
||||||
Deci_Celsius :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
Deci_Celsius :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_celsius procedure group.
|
@(private = "file")
|
||||||
deci_celsius_to_celsius :: #force_inline proc "contextless" (
|
deci_celsius_to_celsius :: #force_inline proc "contextless" (
|
||||||
deci_degrees_celsius: Deci_Celsius($V),
|
deci_degrees_celsius: Deci_Celsius($V),
|
||||||
) -> Celsius(V) where intrinsics.type_is_numeric(V) {
|
) -> Celsius(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Celsius(V){deci_degrees_celsius.v / DECI}
|
return Celsius(V){deci_degrees_celsius.v / DECI}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----- Degrees Fahrenheit ----------------------------------
|
|
||||||
Fahrenheit :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_celsius procedure group.
|
|
||||||
fahrenheit_to_celsius :: #force_inline proc "contextless" (
|
|
||||||
degrees_fahrenheit: Fahrenheit($V),
|
|
||||||
) -> Celsius(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
when intrinsics.type_is_float(V) {
|
|
||||||
return Celsius(V){(degrees_fahrenheit.v - 32.0) * CELSIUS_PER_FAHRENHEIT_DEGREE}
|
|
||||||
} else {
|
|
||||||
return Celsius(V){(degrees_fahrenheit.v - 32) * 5 / 9}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_kelvins procedure group.
|
|
||||||
fahrenheit_to_kelvins :: #force_inline proc "contextless" (
|
|
||||||
degrees_fahrenheit: Fahrenheit($V),
|
|
||||||
) -> Kelvins(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return celsius_to_kelvins(fahrenheit_to_celsius(degrees_fahrenheit))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
// ----- Conversion Overloads ------------------------
|
// ----- Conversion Overloads ------------------------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
to_kelvins :: proc {
|
to_kelvins :: proc {
|
||||||
deci_kelvins_to_kelvins,
|
deci_kelvins_to_kelvins,
|
||||||
celsius_to_kelvins,
|
celsius_to_kelvins,
|
||||||
fahrenheit_to_kelvins,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
to_deci_kelvins :: proc {
|
to_deci_kelvins :: proc {
|
||||||
@@ -145,18 +97,12 @@ to_deci_kelvins :: proc {
|
|||||||
to_celsius :: proc {
|
to_celsius :: proc {
|
||||||
kelvins_to_celsius,
|
kelvins_to_celsius,
|
||||||
deci_celsius_to_celsius,
|
deci_celsius_to_celsius,
|
||||||
fahrenheit_to_celsius,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
to_deci_celsius :: proc {
|
to_deci_celsius :: proc {
|
||||||
celsius_to_deci_celsius,
|
celsius_to_deci_celsius,
|
||||||
}
|
}
|
||||||
|
|
||||||
to_fahrenheit :: proc {
|
|
||||||
celsius_to_fahrenheit,
|
|
||||||
kelvins_to_fahrenheit,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
// ----- Tests ------------------------
|
// ----- Tests ------------------------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -209,51 +155,3 @@ test_deci_celsius_to_celsius :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
testing.expect_value(t, degrees_celsius, Celsius(int){100})
|
testing.expect_value(t, degrees_celsius, Celsius(int){100})
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_celsius_to_fahrenheit :: proc(t: ^testing.T) {
|
|
||||||
degrees_celsius := Celsius(int){100}
|
|
||||||
degrees_fahrenheit := to_fahrenheit(degrees_celsius)
|
|
||||||
|
|
||||||
testing.expect_value(t, degrees_fahrenheit, Fahrenheit(int){212})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_fahrenheit_to_celsius :: proc(t: ^testing.T) {
|
|
||||||
degrees_fahrenheit := Fahrenheit(int){212}
|
|
||||||
degrees_celsius := to_celsius(degrees_fahrenheit)
|
|
||||||
|
|
||||||
testing.expect_value(t, degrees_celsius, Celsius(int){100})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_kelvins_to_fahrenheit :: proc(t: ^testing.T) {
|
|
||||||
kelvins := Kelvins(int){373}
|
|
||||||
degrees_fahrenheit := to_fahrenheit(kelvins)
|
|
||||||
|
|
||||||
testing.expect_value(t, degrees_fahrenheit, Fahrenheit(int){212})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_fahrenheit_to_kelvins :: proc(t: ^testing.T) {
|
|
||||||
degrees_fahrenheit := Fahrenheit(int){212}
|
|
||||||
kelvins := to_kelvins(degrees_fahrenheit)
|
|
||||||
|
|
||||||
testing.expect_value(t, kelvins, Kelvins(int){373})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_celsius_to_fahrenheit_f64 :: proc(t: ^testing.T) {
|
|
||||||
// -40 is the point where the Celsius and Fahrenheit scales coincide. It converts exactly in
|
|
||||||
// f64, so a passing equality here also confirms the 9/5 ratio constant is not lossy.
|
|
||||||
degrees_fahrenheit := to_fahrenheit(Celsius(f64){-40})
|
|
||||||
|
|
||||||
testing.expect_value(t, degrees_fahrenheit, Fahrenheit(f64){-40})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_fahrenheit_to_celsius_f64 :: proc(t: ^testing.T) {
|
|
||||||
degrees_celsius := to_celsius(Fahrenheit(f64){-40})
|
|
||||||
|
|
||||||
testing.expect_value(t, degrees_celsius, Celsius(f64){-40})
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Volts :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_milli_volts procedure group.
|
@(private = "file")
|
||||||
volts_to_milli_volts :: #force_inline proc "contextless" (
|
volts_to_milli_volts :: #force_inline proc "contextless" (
|
||||||
volts: Volts($V),
|
volts: Volts($V),
|
||||||
) -> Milli_Volts(V) where intrinsics.type_is_numeric(V) {
|
) -> Milli_Volts(V) where intrinsics.type_is_numeric(V) {
|
||||||
@@ -19,7 +19,7 @@ Milli_Volts :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_volts procedure group.
|
@(private = "file")
|
||||||
milli_volts_to_volts :: #force_inline proc "contextless" (
|
milli_volts_to_volts :: #force_inline proc "contextless" (
|
||||||
milli_volts: Milli_Volts($V),
|
milli_volts: Milli_Volts($V),
|
||||||
) -> Volts(V) where intrinsics.type_is_numeric(V) {
|
) -> Volts(V) where intrinsics.type_is_numeric(V) {
|
||||||
|
|||||||
+2
-154
@@ -2,127 +2,39 @@ package quantity
|
|||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
LITERS_PER_GALLON :: 3.785411784
|
|
||||||
MICRO_LITERS_PER_GALLON :: 473176473.0 / 125.0
|
|
||||||
|
|
||||||
//----- Liters ----------------------------------
|
//----- Liters ----------------------------------
|
||||||
Liters :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
Liters :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_milli_liters procedure group.
|
@(private = "file")
|
||||||
liters_to_milli_liters :: #force_inline proc "contextless" (
|
liters_to_milli_liters :: #force_inline proc "contextless" (
|
||||||
liters: Liters($V),
|
liters: Liters($V),
|
||||||
) -> Milli_Liters(V) where intrinsics.type_is_numeric(V) {
|
) -> Milli_Liters(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Milli_Liters(V){liters.v * MILLI}
|
return Milli_Liters(V){liters.v * MILLI}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_gallons procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
liters_to_gallons :: #force_inline proc "contextless" (
|
|
||||||
liters: Liters($V),
|
|
||||||
) -> Gallons(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Gallons(V){liters.v / LITERS_PER_GALLON}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_liters procedure group.
|
|
||||||
liters_to_micro_liters :: #force_inline proc "contextless" (
|
|
||||||
liters: Liters($V),
|
|
||||||
) -> Micro_Liters(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Micro_Liters(V){liters.v * MICRO}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Milliliters ----------------------------------
|
//----- Milliliters ----------------------------------
|
||||||
Milli_Liters :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
Milli_Liters :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_liters procedure group.
|
@(private = "file")
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
milli_liters_to_liters :: #force_inline proc "contextless" (
|
milli_liters_to_liters :: #force_inline proc "contextless" (
|
||||||
milli_liters: Milli_Liters($V),
|
milli_liters: Milli_Liters($V),
|
||||||
) -> Liters(V) where intrinsics.type_is_numeric(V) {
|
) -> Liters(V) where intrinsics.type_is_numeric(V) {
|
||||||
return Liters(V){milli_liters.v / MILLI}
|
return Liters(V){milli_liters.v / MILLI}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_micro_liters procedure group.
|
|
||||||
milli_liters_to_micro_liters :: #force_inline proc "contextless" (
|
|
||||||
milli_liters: Milli_Liters($V),
|
|
||||||
) -> Micro_Liters(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Micro_Liters(V){milli_liters.v * (MICRO / MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Microliters ----------------------------------
|
|
||||||
Micro_Liters :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_liters procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_liters_to_liters :: #force_inline proc "contextless" (
|
|
||||||
micro_liters: Micro_Liters($V),
|
|
||||||
) -> Liters(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Liters(V){micro_liters.v / MICRO}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_milli_liters procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_liters_to_milli_liters :: #force_inline proc "contextless" (
|
|
||||||
micro_liters: Micro_Liters($V),
|
|
||||||
) -> Milli_Liters(V) where intrinsics.type_is_numeric(V) {
|
|
||||||
return Milli_Liters(V){micro_liters.v / (MICRO / MILLI)}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_gallons procedure group.
|
|
||||||
@(fast_math = {.Allow_Reciprocal})
|
|
||||||
micro_liters_to_gallons :: #force_inline proc "contextless" (
|
|
||||||
micro_liters: Micro_Liters($V),
|
|
||||||
) -> Gallons(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Gallons(V){micro_liters.v / MICRO_LITERS_PER_GALLON}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Gallons ----------------------------------
|
|
||||||
Gallons :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_liters procedure group.
|
|
||||||
gallons_to_liters :: #force_inline proc "contextless" (
|
|
||||||
gallons: Gallons($V),
|
|
||||||
) -> Liters(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Liters(V){gallons.v * LITERS_PER_GALLON}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_micro_liters procedure group.
|
|
||||||
gallons_to_micro_liters :: #force_inline proc "contextless" (
|
|
||||||
gallons: Gallons($V),
|
|
||||||
) -> Micro_Liters(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Micro_Liters(V){gallons.v * MICRO_LITERS_PER_GALLON}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
// ----- Conversion Overloads ------------------------
|
// ----- Conversion Overloads ------------------------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
to_liters :: proc {
|
to_liters :: proc {
|
||||||
milli_liters_to_liters,
|
milli_liters_to_liters,
|
||||||
micro_liters_to_liters,
|
|
||||||
gallons_to_liters,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
to_milli_liters :: proc {
|
to_milli_liters :: proc {
|
||||||
liters_to_milli_liters,
|
liters_to_milli_liters,
|
||||||
micro_liters_to_milli_liters,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_micro_liters :: proc {
|
|
||||||
liters_to_micro_liters,
|
|
||||||
milli_liters_to_micro_liters,
|
|
||||||
gallons_to_micro_liters,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_gallons :: proc {
|
|
||||||
liters_to_gallons,
|
|
||||||
micro_liters_to_gallons,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -145,67 +57,3 @@ test_milli_liters_to_liters :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
testing.expect_value(t, liters, Liters(int){12})
|
testing.expect_value(t, liters, Liters(int){12})
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_gallons_to_liters :: proc(t: ^testing.T) {
|
|
||||||
gallons := Gallons(f32){1}
|
|
||||||
liters := to_liters(gallons)
|
|
||||||
|
|
||||||
testing.expect(t, liters.v > 3.78 && liters.v < 3.79)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_liters_to_gallons :: proc(t: ^testing.T) {
|
|
||||||
liters := Liters(f32){3.785411784}
|
|
||||||
gallons := to_gallons(liters)
|
|
||||||
|
|
||||||
testing.expect(t, gallons.v > 0.99 && gallons.v < 1.01)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_liters_to_micro_liters :: proc(t: ^testing.T) {
|
|
||||||
liters := Liters(int){12}
|
|
||||||
micro_liters := to_micro_liters(liters)
|
|
||||||
|
|
||||||
testing.expect_value(t, micro_liters, Micro_Liters(int){12_000_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_liters_to_liters :: proc(t: ^testing.T) {
|
|
||||||
micro_liters := Micro_Liters(int){12_000_000}
|
|
||||||
liters := to_liters(micro_liters)
|
|
||||||
|
|
||||||
testing.expect_value(t, liters, Liters(int){12})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_milli_liters_to_micro_liters :: proc(t: ^testing.T) {
|
|
||||||
milli_liters := Milli_Liters(int){5}
|
|
||||||
micro_liters := to_micro_liters(milli_liters)
|
|
||||||
|
|
||||||
testing.expect_value(t, micro_liters, Micro_Liters(int){5_000})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_liters_to_milli_liters :: proc(t: ^testing.T) {
|
|
||||||
micro_liters := Micro_Liters(int){5_000}
|
|
||||||
milli_liters := to_milli_liters(micro_liters)
|
|
||||||
|
|
||||||
testing.expect_value(t, milli_liters, Milli_Liters(int){5})
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_gallons_to_micro_liters :: proc(t: ^testing.T) {
|
|
||||||
gallons := Gallons(f64){1}
|
|
||||||
micro_liters := to_micro_liters(gallons)
|
|
||||||
|
|
||||||
testing.expect(t, micro_liters.v > 3_785_411.783 && micro_liters.v < 3_785_411.785)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_micro_liters_to_gallons :: proc(t: ^testing.T) {
|
|
||||||
micro_liters := Micro_Liters(f64){3_785_411.784}
|
|
||||||
gallons := to_gallons(micro_liters)
|
|
||||||
|
|
||||||
testing.expect(t, gallons.v > 0.9999999 && gallons.v < 1.0000001)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,58 +2,6 @@ package quantity
|
|||||||
|
|
||||||
import "base:intrinsics"
|
import "base:intrinsics"
|
||||||
|
|
||||||
//----- Liters Per Minute ----------------------------------
|
|
||||||
Liters_Per_Minute :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
Liters_Per_Minute :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
||||||
v: V,
|
v: V,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the to_gallons_per_minute procedure group.
|
|
||||||
liters_per_minute_to_gallons_per_minute :: #force_inline proc "contextless" (
|
|
||||||
liters_per_minute: Liters_Per_Minute($V),
|
|
||||||
) -> Gallons_Per_Minute(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Gallons_Per_Minute(V){liters_per_minute.v / LITERS_PER_GALLON}
|
|
||||||
}
|
|
||||||
|
|
||||||
//----- Gallons Per Minute ----------------------------------
|
|
||||||
Gallons_Per_Minute :: struct($V: typeid) where intrinsics.type_is_numeric(V) {
|
|
||||||
v: V,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the to_liters_per_minute procedure group.
|
|
||||||
gallons_per_minute_to_liters_per_minute :: #force_inline proc "contextless" (
|
|
||||||
gallons_per_minute: Gallons_Per_Minute($V),
|
|
||||||
) -> Liters_Per_Minute(V) where intrinsics.type_is_float(V) {
|
|
||||||
return Liters_Per_Minute(V){gallons_per_minute.v * LITERS_PER_GALLON}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----- Conversion Overloads ------------------------
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
to_liters_per_minute :: proc {
|
|
||||||
gallons_per_minute_to_liters_per_minute,
|
|
||||||
}
|
|
||||||
|
|
||||||
to_gallons_per_minute :: proc {
|
|
||||||
liters_per_minute_to_gallons_per_minute,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
// ----- Tests ------------------------
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
|
||||||
import "core:testing"
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_gallons_per_minute_to_liters_per_minute :: proc(t: ^testing.T) {
|
|
||||||
gallons_per_minute := Gallons_Per_Minute(f32){1}
|
|
||||||
liters_per_minute := to_liters_per_minute(gallons_per_minute)
|
|
||||||
|
|
||||||
testing.expect(t, liters_per_minute.v > 3.78 && liters_per_minute.v < 3.79)
|
|
||||||
}
|
|
||||||
|
|
||||||
@(test)
|
|
||||||
test_liters_per_minute_to_gallons_per_minute :: proc(t: ^testing.T) {
|
|
||||||
liters_per_minute := Liters_Per_Minute(f32){3.785411784}
|
|
||||||
gallons_per_minute := to_gallons_per_minute(liters_per_minute)
|
|
||||||
|
|
||||||
testing.expect(t, gallons_per_minute.v > 0.99 && gallons_per_minute.v < 1.01)
|
|
||||||
}
|
|
||||||
|
|||||||
+23
-23
@@ -113,19 +113,19 @@ advance :: proc {
|
|||||||
advance_soa,
|
advance_soa,
|
||||||
}
|
}
|
||||||
|
|
||||||
push_aos :: #force_inline proc(ring: ^Ring($E), element: E) {
|
append_aos :: #force_inline proc(ring: ^Ring($E), element: E) {
|
||||||
ring.data[ring.next_write_index] = element
|
ring.data[ring.next_write_index] = element
|
||||||
advance(ring)
|
advance(ring)
|
||||||
}
|
}
|
||||||
|
|
||||||
push_soa :: #force_inline proc(ring: ^Ring_Soa($E), element: E) {
|
append_soa :: #force_inline proc(ring: ^Ring_Soa($E), element: E) {
|
||||||
ring.data[ring.next_write_index] = element
|
ring.data[ring.next_write_index] = element
|
||||||
advance(ring)
|
advance(ring)
|
||||||
}
|
}
|
||||||
|
|
||||||
push :: proc {
|
append :: proc {
|
||||||
push_aos,
|
append_aos,
|
||||||
push_soa,
|
append_soa,
|
||||||
}
|
}
|
||||||
|
|
||||||
get_aos :: #force_inline proc(ring: Ring($E), index: int) -> ^E {
|
get_aos :: #force_inline proc(ring: Ring($E), index: int) -> ^E {
|
||||||
@@ -202,7 +202,7 @@ test_ring_aos :: proc(t: ^testing.T) {
|
|||||||
defer destroy(&ring)
|
defer destroy(&ring)
|
||||||
|
|
||||||
for i in 1 ..= 5 {
|
for i in 1 ..= 5 {
|
||||||
push(&ring, i)
|
append(&ring, i)
|
||||||
log.debug("Length:", ring.len)
|
log.debug("Length:", ring.len)
|
||||||
log.debug("Start index:", start_index_aos(ring))
|
log.debug("Start index:", start_index_aos(ring))
|
||||||
log.debug("Next write index:", ring.next_write_index)
|
log.debug("Next write index:", ring.next_write_index)
|
||||||
@@ -215,7 +215,7 @@ test_ring_aos :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_aos(ring), 0)
|
testing.expect_value(t, start_index_aos(ring), 0)
|
||||||
|
|
||||||
for i in 6 ..= 15 {
|
for i in 6 ..= 15 {
|
||||||
push(&ring, i)
|
append(&ring, i)
|
||||||
log.debug("Length:", ring.len)
|
log.debug("Length:", ring.len)
|
||||||
log.debug("Start index:", start_index_aos(ring))
|
log.debug("Start index:", start_index_aos(ring))
|
||||||
log.debug("Next write index:", ring.next_write_index)
|
log.debug("Next write index:", ring.next_write_index)
|
||||||
@@ -230,7 +230,7 @@ test_ring_aos :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_aos(ring), 5)
|
testing.expect_value(t, start_index_aos(ring), 5)
|
||||||
|
|
||||||
for i in 15 ..= 25 {
|
for i in 15 ..= 25 {
|
||||||
push(&ring, i)
|
append(&ring, i)
|
||||||
log.debug("Length:", ring.len)
|
log.debug("Length:", ring.len)
|
||||||
log.debug("Start index:", start_index_aos(ring))
|
log.debug("Start index:", start_index_aos(ring))
|
||||||
log.debug("Next write index:", ring.next_write_index)
|
log.debug("Next write index:", ring.next_write_index)
|
||||||
@@ -241,7 +241,7 @@ test_ring_aos :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, get_last(ring)^, 25)
|
testing.expect_value(t, get_last(ring)^, 25)
|
||||||
|
|
||||||
clear(&ring)
|
clear(&ring)
|
||||||
push(&ring, 1)
|
append(&ring, 1)
|
||||||
testing.expect_value(t, ring.len, 1)
|
testing.expect_value(t, ring.len, 1)
|
||||||
testing.expect_value(t, get(ring, 0)^, 1)
|
testing.expect_value(t, get(ring, 0)^, 1)
|
||||||
}
|
}
|
||||||
@@ -256,7 +256,7 @@ test_ring_soa :: proc(t: ^testing.T) {
|
|||||||
defer destroy(&ring)
|
defer destroy(&ring)
|
||||||
|
|
||||||
for i in 1 ..= 5 {
|
for i in 1 ..= 5 {
|
||||||
push(&ring, Ints{i, i})
|
append(&ring, Ints{i, i})
|
||||||
log.debug("Length:", ring.len)
|
log.debug("Length:", ring.len)
|
||||||
log.debug("Start index:", start_index_soa(ring))
|
log.debug("Start index:", start_index_soa(ring))
|
||||||
log.debug("Next write index:", ring.next_write_index)
|
log.debug("Next write index:", ring.next_write_index)
|
||||||
@@ -269,7 +269,7 @@ test_ring_soa :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_soa(ring), 0)
|
testing.expect_value(t, start_index_soa(ring), 0)
|
||||||
|
|
||||||
for i in 6 ..= 15 {
|
for i in 6 ..= 15 {
|
||||||
push(&ring, Ints{i, i})
|
append(&ring, Ints{i, i})
|
||||||
log.debug("Length:", ring.len)
|
log.debug("Length:", ring.len)
|
||||||
log.debug("Start index:", start_index_soa(ring))
|
log.debug("Start index:", start_index_soa(ring))
|
||||||
log.debug("Next write index:", ring.next_write_index)
|
log.debug("Next write index:", ring.next_write_index)
|
||||||
@@ -284,7 +284,7 @@ test_ring_soa :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_soa(ring), 5)
|
testing.expect_value(t, start_index_soa(ring), 5)
|
||||||
|
|
||||||
for i in 15 ..= 25 {
|
for i in 15 ..= 25 {
|
||||||
push(&ring, Ints{i, i})
|
append(&ring, Ints{i, i})
|
||||||
log.debug("Length:", ring.len)
|
log.debug("Length:", ring.len)
|
||||||
log.debug("Start index:", start_index_soa(ring))
|
log.debug("Start index:", start_index_soa(ring))
|
||||||
log.debug("Next write index:", ring.next_write_index)
|
log.debug("Next write index:", ring.next_write_index)
|
||||||
@@ -295,7 +295,7 @@ test_ring_soa :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, get_last(ring), Ints{25, 25})
|
testing.expect_value(t, get_last(ring), Ints{25, 25})
|
||||||
|
|
||||||
clear(&ring)
|
clear(&ring)
|
||||||
push(&ring, Ints{1, 1})
|
append(&ring, Ints{1, 1})
|
||||||
testing.expect_value(t, ring.len, 1)
|
testing.expect_value(t, ring.len, 1)
|
||||||
testing.expect_value(t, get(ring, 0), Ints{1, 1})
|
testing.expect_value(t, get(ring, 0), Ints{1, 1})
|
||||||
}
|
}
|
||||||
@@ -314,7 +314,7 @@ test_ring_aos_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_aos(ring), 0)
|
testing.expect_value(t, start_index_aos(ring), 0)
|
||||||
|
|
||||||
// Partial fill (3 / 7).
|
// Partial fill (3 / 7).
|
||||||
for i in 1 ..= 3 do push(&ring, i)
|
for i in 1 ..= 3 do append(&ring, i)
|
||||||
testing.expect_value(t, ring.len, 3)
|
testing.expect_value(t, ring.len, 3)
|
||||||
testing.expect_value(t, ring.next_write_index, 3)
|
testing.expect_value(t, ring.next_write_index, 3)
|
||||||
testing.expect_value(t, start_index_aos(ring), 0)
|
testing.expect_value(t, start_index_aos(ring), 0)
|
||||||
@@ -324,7 +324,7 @@ test_ring_aos_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
// Fill exactly to capacity. Pushing element 7 must make len == cap
|
// Fill exactly to capacity. Pushing element 7 must make len == cap
|
||||||
// AND wrap next_write_index from 6 back to 0 in the same step.
|
// AND wrap next_write_index from 6 back to 0 in the same step.
|
||||||
for i in 4 ..= 7 do push(&ring, i)
|
for i in 4 ..= 7 do append(&ring, i)
|
||||||
testing.expect_value(t, ring.len, 7)
|
testing.expect_value(t, ring.len, 7)
|
||||||
testing.expect_value(t, ring.next_write_index, 0)
|
testing.expect_value(t, ring.next_write_index, 0)
|
||||||
testing.expect_value(t, start_index_aos(ring), 0)
|
testing.expect_value(t, start_index_aos(ring), 0)
|
||||||
@@ -333,7 +333,7 @@ test_ring_aos_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, get_last(ring)^, 7)
|
testing.expect_value(t, get_last(ring)^, 7)
|
||||||
|
|
||||||
// First overwrite — oldest element shifts by one.
|
// First overwrite — oldest element shifts by one.
|
||||||
push(&ring, 8)
|
append(&ring, 8)
|
||||||
testing.expect_value(t, ring.len, 7)
|
testing.expect_value(t, ring.len, 7)
|
||||||
testing.expect_value(t, ring.next_write_index, 1)
|
testing.expect_value(t, ring.next_write_index, 1)
|
||||||
testing.expect_value(t, start_index_aos(ring), 1)
|
testing.expect_value(t, start_index_aos(ring), 1)
|
||||||
@@ -344,7 +344,7 @@ test_ring_aos_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
// Stress: 3 more complete wrap cycles (21 more pushes).
|
// Stress: 3 more complete wrap cycles (21 more pushes).
|
||||||
// After 29 total pushes, ring contains the last 7 (23..=29),
|
// After 29 total pushes, ring contains the last 7 (23..=29),
|
||||||
// and next_write_index = 29 mod 7 = 1.
|
// and next_write_index = 29 mod 7 = 1.
|
||||||
for i in 9 ..= 29 do push(&ring, i)
|
for i in 9 ..= 29 do append(&ring, i)
|
||||||
testing.expect_value(t, ring.len, 7)
|
testing.expect_value(t, ring.len, 7)
|
||||||
testing.expect_value(t, ring.next_write_index, 1)
|
testing.expect_value(t, ring.next_write_index, 1)
|
||||||
testing.expect_value(t, start_index_aos(ring), 1)
|
testing.expect_value(t, start_index_aos(ring), 1)
|
||||||
@@ -360,7 +360,7 @@ test_ring_aos_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_aos(ring), 0)
|
testing.expect_value(t, start_index_aos(ring), 0)
|
||||||
|
|
||||||
// Single-element edge case: get_last(len==1) routes through get(ring, 0).
|
// Single-element edge case: get_last(len==1) routes through get(ring, 0).
|
||||||
push(&ring, 42)
|
append(&ring, 42)
|
||||||
testing.expect_value(t, ring.len, 1)
|
testing.expect_value(t, ring.len, 1)
|
||||||
testing.expect_value(t, ring.next_write_index, 1)
|
testing.expect_value(t, ring.next_write_index, 1)
|
||||||
testing.expect_value(t, get(ring, 0)^, 42)
|
testing.expect_value(t, get(ring, 0)^, 42)
|
||||||
@@ -385,7 +385,7 @@ test_ring_soa_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_soa(ring), 0)
|
testing.expect_value(t, start_index_soa(ring), 0)
|
||||||
|
|
||||||
// Partial fill (3 / 7).
|
// Partial fill (3 / 7).
|
||||||
for i in 1 ..= 3 do push(&ring, Ints{i, i})
|
for i in 1 ..= 3 do append(&ring, Ints{i, i})
|
||||||
testing.expect_value(t, ring.len, 3)
|
testing.expect_value(t, ring.len, 3)
|
||||||
testing.expect_value(t, ring.next_write_index, 3)
|
testing.expect_value(t, ring.next_write_index, 3)
|
||||||
testing.expect_value(t, start_index_soa(ring), 0)
|
testing.expect_value(t, start_index_soa(ring), 0)
|
||||||
@@ -395,7 +395,7 @@ test_ring_soa_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
// Fill exactly to capacity. Pushing element 7 must make len == cap
|
// Fill exactly to capacity. Pushing element 7 must make len == cap
|
||||||
// AND wrap next_write_index from 6 back to 0 in the same step.
|
// AND wrap next_write_index from 6 back to 0 in the same step.
|
||||||
for i in 4 ..= 7 do push(&ring, Ints{i, i})
|
for i in 4 ..= 7 do append(&ring, Ints{i, i})
|
||||||
testing.expect_value(t, ring.len, 7)
|
testing.expect_value(t, ring.len, 7)
|
||||||
testing.expect_value(t, ring.next_write_index, 0)
|
testing.expect_value(t, ring.next_write_index, 0)
|
||||||
testing.expect_value(t, start_index_soa(ring), 0)
|
testing.expect_value(t, start_index_soa(ring), 0)
|
||||||
@@ -404,7 +404,7 @@ test_ring_soa_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, get_last(ring), Ints{7, 7})
|
testing.expect_value(t, get_last(ring), Ints{7, 7})
|
||||||
|
|
||||||
// First overwrite — oldest element shifts by one.
|
// First overwrite — oldest element shifts by one.
|
||||||
push(&ring, Ints{8, 8})
|
append(&ring, Ints{8, 8})
|
||||||
testing.expect_value(t, ring.len, 7)
|
testing.expect_value(t, ring.len, 7)
|
||||||
testing.expect_value(t, ring.next_write_index, 1)
|
testing.expect_value(t, ring.next_write_index, 1)
|
||||||
testing.expect_value(t, start_index_soa(ring), 1)
|
testing.expect_value(t, start_index_soa(ring), 1)
|
||||||
@@ -415,7 +415,7 @@ test_ring_soa_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
// Stress: 3 more complete wrap cycles (21 more pushes).
|
// Stress: 3 more complete wrap cycles (21 more pushes).
|
||||||
// After 29 total pushes, ring contains the last 7 (23..=29),
|
// After 29 total pushes, ring contains the last 7 (23..=29),
|
||||||
// and next_write_index = 29 mod 7 = 1.
|
// and next_write_index = 29 mod 7 = 1.
|
||||||
for i in 9 ..= 29 do push(&ring, Ints{i, i})
|
for i in 9 ..= 29 do append(&ring, Ints{i, i})
|
||||||
testing.expect_value(t, ring.len, 7)
|
testing.expect_value(t, ring.len, 7)
|
||||||
testing.expect_value(t, ring.next_write_index, 1)
|
testing.expect_value(t, ring.next_write_index, 1)
|
||||||
testing.expect_value(t, start_index_soa(ring), 1)
|
testing.expect_value(t, start_index_soa(ring), 1)
|
||||||
@@ -431,7 +431,7 @@ test_ring_soa_init_from_slice :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, start_index_soa(ring), 0)
|
testing.expect_value(t, start_index_soa(ring), 0)
|
||||||
|
|
||||||
// Single-element edge case: get_last(len==1) routes through get(ring, 0).
|
// Single-element edge case: get_last(len==1) routes through get(ring, 0).
|
||||||
push(&ring, Ints{42, 42})
|
append(&ring, Ints{42, 42})
|
||||||
testing.expect_value(t, ring.len, 1)
|
testing.expect_value(t, ring.len, 1)
|
||||||
testing.expect_value(t, ring.next_write_index, 1)
|
testing.expect_value(t, ring.next_write_index, 1)
|
||||||
testing.expect_value(t, get(ring, 0), Ints{42, 42})
|
testing.expect_value(t, get(ring, 0), Ints{42, 42})
|
||||||
|
|||||||
Vendored
+41
-617
File diff suppressed because it is too large
Load Diff
Vendored
+4
-4
@@ -68,9 +68,9 @@ main :: proc() {
|
|||||||
db_handle: mdb.Dbi
|
db_handle: mdb.Dbi
|
||||||
// Put transaction
|
// Put transaction
|
||||||
key := 7
|
key := 7
|
||||||
key_val := mdb.pod_val(&key)
|
key_val := mdb.blittable_val(&key)
|
||||||
put_data := 12
|
put_data := 12
|
||||||
put_data_val := mdb.pod_val(&put_data)
|
put_data_val := mdb.blittable_val(&put_data)
|
||||||
mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle))
|
mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle))
|
||||||
mdb.panic_on_err(mdb.dbi_open(txn_handle, nil, {}, &db_handle))
|
mdb.panic_on_err(mdb.dbi_open(txn_handle, nil, {}, &db_handle))
|
||||||
mdb.panic_on_err(mdb.put(txn_handle, db_handle, &key_val, &put_data_val, {}))
|
mdb.panic_on_err(mdb.put(txn_handle, db_handle, &key_val, &put_data_val, {}))
|
||||||
@@ -80,7 +80,7 @@ main :: proc() {
|
|||||||
data_val: mdb.Val
|
data_val: mdb.Val
|
||||||
mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle))
|
mdb.panic_on_err(mdb.txn_begin(environment, nil, {}, &txn_handle))
|
||||||
mdb.panic_on_err(mdb.get(txn_handle, db_handle, &key_val, &data_val))
|
mdb.panic_on_err(mdb.get(txn_handle, db_handle, &key_val, &data_val))
|
||||||
data_cpy := mdb.pod_copy(data_val, int)
|
data_cpy := mdb.blittable_copy(&data_val, int)
|
||||||
mdb.txn_abort(txn_handle)
|
mdb.panic_on_err(mdb.txn_commit(txn_handle))
|
||||||
fmt.println("Get result:", data_cpy)
|
fmt.println("Get result:", data_cpy)
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+15
-52
@@ -169,86 +169,58 @@ import "core:fmt"
|
|||||||
import "core:reflect"
|
import "core:reflect"
|
||||||
import "core:sys/posix"
|
import "core:sys/posix"
|
||||||
|
|
||||||
import b "../../basic"
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
// ----- Added Odin Helpers ------------------------
|
// ----- Added Odin Helpers ------------------------
|
||||||
// ---------------------------------------------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// Wrap a POD value's bytes as an LMDB Val.
|
// Wrap a blittable value's bytes as an LMDB Val.
|
||||||
// T must be a contiguous type with no indirection (no pointers, slices, strings, maps, etc.).
|
// T must be a contiguous type with no indirection (no pointers, slices, strings, maps, etc.).
|
||||||
pod_val :: #force_inline proc(val_ptr: ^$T) -> Val {
|
blittable_val :: #force_inline proc(val_ptr: ^$T) -> Val {
|
||||||
when ODIN_DEBUG {
|
|
||||||
fmt.assertf(
|
fmt.assertf(
|
||||||
reflect.has_no_indirections(type_info_of(T)),
|
reflect.has_no_indirections(type_info_of(T)),
|
||||||
"pod_val: type '%v' contains indirection and cannot be stored directly in LMDB",
|
"blitval: type '%v' contains indirection and cannot be stored directly in LMDB",
|
||||||
typeid_of(T),
|
typeid_of(T),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
return Val{size_of(T), val_ptr}
|
return Val{size_of(T), val_ptr}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a POD T out of the LMDB memory map by copying it into caller
|
// Reads a blittable T out of the LMDB memory map by copying it into caller
|
||||||
// storage. The returned T has no lifetime tie to the transaction.
|
// storage. The returned T has no lifetime tie to the transaction.
|
||||||
pod_copy :: #force_inline proc(val: Val, $T: typeid) -> T {
|
blittable_copy :: #force_inline proc(val: ^Val, $T: typeid) -> T {
|
||||||
when ODIN_DEBUG {
|
|
||||||
fmt.assertf(
|
fmt.assertf(
|
||||||
reflect.has_no_indirections(type_info_of(T)),
|
reflect.has_no_indirections(type_info_of(T)),
|
||||||
"pod_copy: type '%v' contains indirection and cannot be read directly from LMDB",
|
"blitval_copy: type '%v' contains indirection and cannot be read directly from LMDB",
|
||||||
typeid_of(T),
|
typeid_of(T),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
when b.ODIN_BOUNDS_CHECK {
|
|
||||||
fmt.assertf(
|
|
||||||
val.size == size_of(T),
|
|
||||||
"size_of(%v) (%v) != val.size (%v)",
|
|
||||||
typeid_of(T),
|
|
||||||
size_of(T),
|
|
||||||
val.size,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (cast(^T)val.data)^
|
return (cast(^T)val.data)^
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zero-copy pointer view into the LMDB memory map as a ^T.
|
// Zero-copy pointer view into the LMDB memory map as a ^T.
|
||||||
// Useful for large POD types where you want to read individual fields
|
// Useful for large blittable types where you want to read individual fields
|
||||||
// without copying the entire value (e.g. ptr.timestamp, ptr.flags).
|
// without copying the entire value (e.g. ptr.timestamp, ptr.flags).
|
||||||
// MUST NOT be written through — writes either segfault (default env mode)
|
// MUST NOT be written through — writes either segfault (default env mode)
|
||||||
// or silently corrupt the database (ENV_WRITEMAP).
|
// or silently corrupt the database (ENV_WRITEMAP).
|
||||||
// MUST NOT be retained past txn_commit, txn_abort, or any subsequent write
|
// MUST NOT be retained past txn_commit, txn_abort, or any subsequent write
|
||||||
// operation on the same env — the pointer is invalidated.
|
// operation on the same env — the pointer is invalidated.
|
||||||
pod_view :: #force_inline proc(val: Val, $T: typeid) -> ^T {
|
blittable_view :: #force_inline proc(val: ^Val, $T: typeid) -> ^T {
|
||||||
when ODIN_DEBUG {
|
|
||||||
fmt.assertf(
|
fmt.assertf(
|
||||||
reflect.has_no_indirections(type_info_of(T)),
|
reflect.has_no_indirections(type_info_of(T)),
|
||||||
"pod_view: type '%v' contains indirection and cannot be viewed directly from LMDB",
|
"blitval_view: type '%v' contains indirection and cannot be viewed directly from LMDB",
|
||||||
typeid_of(T),
|
typeid_of(T),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
when b.ODIN_BOUNDS_CHECK {
|
|
||||||
fmt.assertf(
|
|
||||||
val.size == size_of(T),
|
|
||||||
"size_of(%v) (%v) != val.size (%v)",
|
|
||||||
typeid_of(T),
|
|
||||||
size_of(T),
|
|
||||||
val.size,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return cast(^T)val.data
|
return cast(^T)val.data
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap a slice of POD elements as an LMDB Val for use with put/get.
|
// Wrap a slice of blittable elements as an LMDB Val for use with put/get.
|
||||||
// T must be a contiguous type with no indirection.
|
// T must be a contiguous type with no indirection.
|
||||||
// The caller's slice must remain valid (not freed, not resized) for the
|
// The caller's slice must remain valid (not freed, not resized) for the
|
||||||
// duration of the put call that consumes this Val.
|
// duration of the put call that consumes this Val.
|
||||||
pod_slice_val :: #force_inline proc(s: []$T) -> Val {
|
slice_val :: #force_inline proc(s: []$T) -> Val {
|
||||||
when ODIN_DEBUG {
|
|
||||||
fmt.assertf(
|
fmt.assertf(
|
||||||
reflect.has_no_indirections(type_info_of(T)),
|
reflect.has_no_indirections(type_info_of(T)),
|
||||||
"pod_slice_val: element type '%v' contains indirection and cannot be stored directly in LMDB",
|
"slice_val: element type '%v' contains indirection and cannot be stored directly in LMDB",
|
||||||
typeid_of(T),
|
typeid_of(T),
|
||||||
)
|
)
|
||||||
}
|
|
||||||
return Val{uint(len(s) * size_of(T)), raw_data(s)}
|
return Val{uint(len(s) * size_of(T)), raw_data(s)}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,21 +231,12 @@ pod_slice_val :: #force_inline proc(s: []$T) -> Val {
|
|||||||
// MUST be copied (e.g. slice.clone) if it needs to outlive the current
|
// MUST be copied (e.g. slice.clone) if it needs to outlive the current
|
||||||
// transaction; the view is invalidated by txn_commit, txn_abort, or any
|
// transaction; the view is invalidated by txn_commit, txn_abort, or any
|
||||||
// subsequent write operation on the same env.
|
// subsequent write operation on the same env.
|
||||||
pod_slice_view :: #force_inline proc(val: Val, $T: typeid) -> []T {
|
slice_view :: #force_inline proc(val: ^Val, $T: typeid) -> []T {
|
||||||
when ODIN_DEBUG {
|
|
||||||
fmt.assertf(
|
fmt.assertf(
|
||||||
reflect.has_no_indirections(type_info_of(T)),
|
reflect.has_no_indirections(type_info_of(T)),
|
||||||
"pod_slice_view: element type '%v' contains indirection and cannot be read directly from LMDB",
|
"slice_view: element type '%v' contains indirection and cannot be read directly from LMDB",
|
||||||
typeid_of(T),
|
typeid_of(T),
|
||||||
)
|
)
|
||||||
fmt.assertf(
|
|
||||||
val.size % size_of(T) == 0,
|
|
||||||
"pod_slice_view: val.size (%v) is not a multiple of size_of(%v) (%v)",
|
|
||||||
val.size,
|
|
||||||
typeid_of(T),
|
|
||||||
size_of(T),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (cast([^]T)val.data)[:val.size / size_of(T)]
|
return (cast([^]T)val.data)[:val.size / size_of(T)]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +253,7 @@ string_val :: #force_inline proc(s: string) -> Val {
|
|||||||
// MUST be copied (e.g. strings.clone) if it needs to outlive the current
|
// MUST be copied (e.g. strings.clone) if it needs to outlive the current
|
||||||
// transaction; the view is invalidated by txn_commit, txn_abort, or any
|
// transaction; the view is invalidated by txn_commit, txn_abort, or any
|
||||||
// subsequent write operation on the same env.
|
// subsequent write operation on the same env.
|
||||||
string_view :: #force_inline proc(val: Val) -> string {
|
string_view :: #force_inline proc(val: ^Val) -> string {
|
||||||
return string((cast([^]u8)val.data)[:val.size])
|
return string((cast([^]u8)val.data)[:val.size])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user