1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
3
4package purego
5
6import (
7 "math"
8 "reflect"
9 "testing"
10)
11
12func TestPackFloat32PairMasksLowWord(t *testing.T) {
13 low := uintptr(0xDEADBEEF00000000 | uint64(math.Float32bits(1.5)))
14 high := uintptr(math.Float32bits(2.5))
15 want := uintptr(uint64(math.Float32bits(2.5))<<32 | uint64(math.Float32bits(1.5)))
16 if got := packFloat32Pair(low, high); got != want {
17 t.Fatalf("packFloat32Pair() = %#x, want %#x", got, want)
18 }
19}
20
21func TestHighFloat32Bits(t *testing.T) {
22 word := uintptr(uint64(math.Float32bits(3.25)) << 32)
23 if got, want := highFloat32Bits(word), math.Float32bits(3.25); got != want {
24 t.Fatalf("highFloat32Bits() = %#x, want %#x", got, want)
25 }
26}
27
28func TestFlushPackedWordResetsState(t *testing.T) {
29 var val uint64 = 0x11223344
30 shift := byte(32)
31 flushed := false
32 var gotFloat uintptr
33 flushPackedWord(&val, &shift, &flushed, true, func(v uintptr) { gotFloat = v }, func(uintptr) {
34 t.Fatal("unexpected integer flush")
35 })
36 if !flushed || val != 0 || shift != 0 {
37 t.Fatalf("flushPackedWord() left state dirty: flushed=%v val=%#x shift=%d", flushed, val, shift)
38 }
39 if gotFloat != 0x11223344 {
40 t.Fatalf("flushPackedWord() flushed %#x, want %#x", gotFloat, uintptr(0x11223344))
41 }
42}
43
44func TestFlushPackedWordResetsStateForIntegerBranch(t *testing.T) {
45 var val uint64 = 0x55667788
46 shift := byte(16)
47 flushed := false
48 var gotInt uintptr
49 flushPackedWord(&val, &shift, &flushed, false, func(uintptr) {
50 t.Fatal("unexpected float flush")
51 }, func(v uintptr) { gotInt = v })
52 if !flushed || val != 0 || shift != 0 {
53 t.Fatalf("flushPackedWord() left state dirty: flushed=%v val=%#x shift=%d", flushed, val, shift)
54 }
55 if gotInt != 0x55667788 {
56 t.Fatalf("flushPackedWord() flushed %#x, want %#x", gotInt, uintptr(0x55667788))
57 }
58}
59
60func TestStableValuePointerCopiesUnaddressableValue(t *testing.T) {
61 type sample struct{ X uint32 }
62 ptr, keepAlive := stableValuePointer(reflect.ValueOf(sample{X: 7}))
63 if keepAlive == nil {
64 t.Fatal("expected keepAlive copy for unaddressable value")
65 }
66 if got := *(*uint32)(ptr); got != 7 {
67 t.Fatalf("stableValuePointer() copied %#x, want %#x", got, uint32(7))
68 }
69}
70
71func TestStableValuePointerUsesAddressableValue(t *testing.T) {
72 type sample struct{ X uint32 }
73 value := sample{X: 9}
74 ptr, keepAlive := stableValuePointer(reflect.ValueOf(&value).Elem())
75 if keepAlive != nil {
76 t.Fatal("expected nil keepAlive for addressable value")
77 }
78 if got := *(*uint32)(ptr); got != 9 {
79 t.Fatalf("stableValuePointer() pointed to %#x, want %#x", got, uint32(9))
80 }
81}
82
83func TestIsAllSameFloatRejectsNestedMixedStruct(t *testing.T) {
84 type inner struct{ X, Y float32 }
85 type mixed struct {
86 Inner inner
87 Tail int32
88 }
89 allFloats, fields := isAllSameFloat(reflect.TypeOf(mixed{}))
90 if allFloats {
91 t.Fatalf("isAllSameFloat() = true for mixed nested struct with %d fields", fields)
92 }
93}
94
95func TestIsAllSameFloatAcceptsNestedFloatStruct(t *testing.T) {
96 type inner struct{ X, Y float32 }
97 type outer struct {
98 Left inner
99 Right inner
100 }
101 allFloats, fields := isAllSameFloat(reflect.TypeOf(outer{}))
102 if !allFloats || fields != 4 {
103 t.Fatalf("isAllSameFloat() = (%v, %d), want (true, 4)", allFloats, fields)
104 }
105}