aboutsummaryrefslogtreecommitdiffstats
path: root/util/system/unaligned_mem_ut.cpp
blob: 48c547dc5c7fc59622dd16d67f68c6d417d46309 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include "unaligned_mem.h"

#include <library/cpp/testing/benchmark/bench.h> 
#include <library/cpp/testing/unittest/registar.h> 

#include <util/system/compiler.h>

#ifdef Y_HAVE_INT128
namespace {
    struct TUInt128 {
        bool operator==(const TUInt128& other) const {
            return x == other.x;
        }

        ui64 Low() const {
            return (ui64)x;
        }

        ui64 High() const {
            return (ui64)(x >> 64);
        }

        static TUInt128 Max() {
            return {~(__uint128_t)0};
        }

        __uint128_t x;
    };
}
#endif

Y_UNIT_TEST_SUITE(UnalignedMem) {
    Y_UNIT_TEST(TestReadWrite) {
        alignas(ui64) char buf[100];

        WriteUnaligned<ui16>(buf + 1, (ui16)1);
        WriteUnaligned<ui32>(buf + 1 + 2, (ui32)2);
        WriteUnaligned<ui64>(buf + 1 + 2 + 4, (ui64)3);

        UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui16>(buf + 1), 1);
        UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui32>(buf + 1 + 2), 2);
        UNIT_ASSERT_VALUES_EQUAL(ReadUnaligned<ui64>(buf + 1 + 2 + 4), 3);
    }

    Y_UNIT_TEST(TestReadWriteRuntime) {
        // Unlike the test above, this test avoids compile-time execution by a smart compiler.
        // It is required to catch the SIGSEGV in case compiler emits an alignment-sensitive instruction.

        alignas(ui64) static char buf[100] = {0}; // static is required for Clobber to work

        WriteUnaligned<ui16>(buf + 1, (ui16)1);
        WriteUnaligned<ui32>(buf + 1 + 2, (ui32)2);
        WriteUnaligned<ui64>(buf + 1 + 2 + 4, (ui64)3);
        NBench::Clobber();

        auto val1 = ReadUnaligned<ui16>(buf + 1);
        auto val2 = ReadUnaligned<ui32>(buf + 1 + 2);
        auto val3 = ReadUnaligned<ui64>(buf + 1 + 2 + 4);

        Y_DO_NOT_OPTIMIZE_AWAY(&val1);
        Y_DO_NOT_OPTIMIZE_AWAY(&val2);
        Y_DO_NOT_OPTIMIZE_AWAY(&val3);
        Y_DO_NOT_OPTIMIZE_AWAY(val1);
        Y_DO_NOT_OPTIMIZE_AWAY(val2);
        Y_DO_NOT_OPTIMIZE_AWAY(val3);

        UNIT_ASSERT_VALUES_EQUAL(val1, 1);
        UNIT_ASSERT_VALUES_EQUAL(val2, 2);
        UNIT_ASSERT_VALUES_EQUAL(val3, 3);
    }
#ifdef Y_HAVE_INT128
    Y_UNIT_TEST(TestReadWrite128) {
        alignas(TUInt128) char buf[100] = {0};

        WriteUnaligned<TUInt128>(buf + 1, TUInt128::Max());
        auto val = ReadUnaligned<TUInt128>(buf + 1);
        UNIT_ASSERT(val == TUInt128::Max());
    }
    Y_UNIT_TEST(TestReadWriteRuntime128) {
        // Unlike the test above, this test avoids compile-time execution by a smart compiler.
        // It is required to catch the SIGSEGV in case compiler emits an alignment-sensitive instruction.

        alignas(TUInt128) static char buf[100] = {0}; // static is required for Clobber to work

        WriteUnaligned<TUInt128>(buf + 1, TUInt128::Max());
        NBench::Clobber();

        auto val = ReadUnaligned<TUInt128>(buf + 1);
        Y_DO_NOT_OPTIMIZE_AWAY(&val);
        Y_DO_NOT_OPTIMIZE_AWAY(val.Low());
        Y_DO_NOT_OPTIMIZE_AWAY(val.High());

        UNIT_ASSERT(val == TUInt128::Max());
    }
#endif
}