File: crc_test.c

package info (click to toggle)
aws-crt-python 0.24.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 75,932 kB
  • sloc: ansic: 418,984; python: 23,626; makefile: 6,035; sh: 4,075; ruby: 208; java: 82; perl: 73; cpp: 25; xml: 11
file content (202 lines) | stat: -rw-r--r-- 8,545 bytes parent folder | download
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/**
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

#include <aws/checksums/crc.h>
#include <aws/checksums/private/crc_priv.h>
#include <aws/checksums/private/crc_util.h>

#include <aws/common/device_random.h>

#include <aws/testing/aws_test_harness.h>

static const uint8_t DATA_32_ZEROS[32] = {0};
static const uint8_t DATA_32_VALUES[32] = {0,  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};

static const uint8_t TEST_VECTOR[] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};

// The polynomial used for CRC32 (in bit-reflected form)
static const uint32_t POLY_CRC32 = 0xedb88320;
// Any input with the CRC32 of that input appended should produce this CRC32 value. (Note: inverting the bits)
static const uint32_t RESIDUE_CRC32 = ~0xdebb20e3;
static const uint32_t KNOWN_CRC32_32_ZEROES = 0x190A55AD;
static const uint32_t KNOWN_CRC32_32_VALUES = 0x91267E8A;
static const uint32_t KNOWN_CRC32_TEST_VECTOR = 0xCBF43926;

// The polynomial used for CRC32C (in bit-reflected form)
static const uint32_t POLY_CRC32C = 0x82f63b78;
// Any input with the CRC32c of that input appended should produce this CRC32c value. (Note: inverting the bits)
static const uint32_t RESIDUE_CRC32C = ~0xb798b438;
static const uint32_t KNOWN_CRC32C_32_ZEROES = 0x8A9136AA;
static const uint32_t KNOWN_CRC32C_32_VALUES = 0x46DD794E;
static const uint32_t KNOWN_CRC32C_TEST_VECTOR = 0xE3069283;

typedef uint32_t(crc_fn)(const uint8_t *input, int length, uint32_t previousCrc32);
#define CRC_FUNC_NAME(crc_func) #crc_func, crc_func
#define DATA_NAME(dataset) #dataset, dataset, sizeof(dataset)
#define TEST_BUFFER_SIZE 2048 + 64

// Slow reference implementation that computes a 32-bit bit-reflected/bit-inverted CRC using the provided polynomial.
static uint32_t s_crc_32_reference(const uint8_t *input, int length, const uint32_t previousCrc, uint32_t polynomial) {
    uint32_t crc = ~previousCrc;
    while (length-- > 0) {
        crc ^= *input++;
        for (int j = 8; j > 0; --j) {
            crc = (crc >> 1) ^ ((((crc & 1) ^ 1) - 1) & polynomial);
        }
    }
    return ~crc;
}

// Very, very slow reference implementation that computes a CRC32.
static uint32_t s_crc32_reference(const uint8_t *input, int length, const uint32_t previousCrc) {
    return s_crc_32_reference(input, length, previousCrc, POLY_CRC32);
}

// Very, very slow reference implementation that computes a CRC32c.
static uint32_t s_crc32c_reference(const uint8_t *input, int length, const uint32_t previousCrc) {
    return s_crc_32_reference(input, length, previousCrc, POLY_CRC32C);
}

/* Makes sure that the specified crc function produces the expected results for known input and output */
static int s_test_known_crc_32(
    const char *func_name,
    crc_fn *func,
    const char *data_name,
    const uint8_t *input,
    const size_t length,
    const uint32_t expected_crc,
    const uint32_t expected_residue) {

    uint32_t result = func(input, (int)length, 0);
    ASSERT_HEX_EQUALS(expected_crc, result, "%s(%s)", func_name, data_name);

    uint32_t result_le = aws_bswap32_if_be(result);
    // Compute the residue of the buffer (the CRC of the buffer plus its CRC) - will always be a constant value
    uint32_t residue = (uint32_t)func((const uint8_t *)&result_le, 4, result); // assuming little endian
    ASSERT_HEX_EQUALS(expected_residue, residue, "len %d residue %s(%s)", length, func_name, data_name);

    // chain the crc computation so 2 calls each operate on about 1/2 of the buffer
    uint32_t crc1 = func(input, (int)(length / 2), 0);
    result = func(input + (length / 2), (int)(length - length / 2), crc1);
    ASSERT_HEX_EQUALS(expected_crc, result, "chaining %s(%s)", func_name, data_name);

    crc1 = 0;
    for (size_t i = 0; i < length; ++i) {
        crc1 = func(input + i, 1, crc1);
    }
    ASSERT_HEX_EQUALS(expected_crc, crc1, "one byte at a time %s(%s)", func_name, data_name);

    return AWS_OP_SUCCESS;
}

/* helper function that tests increasing input data lengths vs the reference crc function */
static int s_test_vs_reference_crc_32(
    struct aws_allocator *allocator,
    uint32_t polynomial,
    uint32_t residue,
    const char *func_name,
    crc_fn *func) {

    int res = 0;

    struct aws_byte_buf test_buf;
    ASSERT_SUCCESS(aws_byte_buf_init(&test_buf, allocator, TEST_BUFFER_SIZE));

    // Spin through buffer offsets
    for (int off = 0; off < 16; off++) {
        // Fill the test buffer with different values for each iteration
        aws_byte_buf_write_u8_n(&test_buf, (uint8_t)off + 129, test_buf.capacity - test_buf.len);
        uint32_t expected = 0;
        int len = 1;
        // Spin through input data lengths
        for (int i = 0; i < (TEST_BUFFER_SIZE - off) && !res; i++, len++) {
            test_buf.buffer[off + i] = (uint8_t)((i + 1) * 131);
            // Compute the expected CRC one byte at a time using the reference function
            expected = s_crc_32_reference(&test_buf.buffer[off + i], 1, expected, polynomial);
            // Recompute the full CRC of the buffer at each offset and length and compare against expected value
            res |= s_test_known_crc_32(func_name, func, "test_buffer", &test_buf.buffer[off], len, expected, residue);
            if (res != 0) {
                continue;
            }
        }
        aws_byte_buf_reset(&test_buf, false);
    }
    aws_byte_buf_clean_up(&test_buf);

    return res;
}

/* helper function that groups crc32 tests*/
static int s_test_known_crc32(struct aws_allocator *allocator, const char *func_name, crc_fn *func) {
    int res = 0;
    res |= s_test_known_crc_32(func_name, func, DATA_NAME(DATA_32_ZEROS), KNOWN_CRC32_32_ZEROES, RESIDUE_CRC32);
    res |= s_test_known_crc_32(func_name, func, DATA_NAME(DATA_32_VALUES), KNOWN_CRC32_32_VALUES, RESIDUE_CRC32);
    res |= s_test_known_crc_32(func_name, func, DATA_NAME(TEST_VECTOR), KNOWN_CRC32_TEST_VECTOR, RESIDUE_CRC32);
    if (func != s_crc32_reference) {
        res |= s_test_vs_reference_crc_32(allocator, POLY_CRC32, RESIDUE_CRC32, func_name, func);
    }
    return res;
}

/* helper function that groups crc32c tests*/
static int s_test_known_crc32c(struct aws_allocator *allocator, const char *func_name, crc_fn *func) {
    int res = 0;

    res |= s_test_known_crc_32(func_name, func, DATA_NAME(DATA_32_ZEROS), KNOWN_CRC32C_32_ZEROES, RESIDUE_CRC32C);
    res |= s_test_known_crc_32(func_name, func, DATA_NAME(DATA_32_VALUES), KNOWN_CRC32C_32_VALUES, RESIDUE_CRC32C);
    res |= s_test_known_crc_32(func_name, func, DATA_NAME(TEST_VECTOR), KNOWN_CRC32C_TEST_VECTOR, RESIDUE_CRC32C);
    if (func != s_crc32c_reference) {
        res |= s_test_vs_reference_crc_32(allocator, POLY_CRC32C, RESIDUE_CRC32C, func_name, func);
    }

    return res;
}

/**
 * Quick sanity check of some known CRC values for known input.
 * The reference functions are included in these tests to verify that they aren't obviously broken.
 */
static int s_test_crc32c(struct aws_allocator *allocator, void *ctx) {
    (void)ctx;

    int res = 0;

    res |= s_test_known_crc32c(allocator, CRC_FUNC_NAME(s_crc32c_reference));
    res |= s_test_known_crc32c(allocator, CRC_FUNC_NAME(aws_checksums_crc32c_sw));
    res |= s_test_known_crc32c(allocator, CRC_FUNC_NAME(aws_checksums_crc32c));

    return res;
}
AWS_TEST_CASE(test_crc32c, s_test_crc32c)

static int s_test_crc32(struct aws_allocator *allocator, void *ctx) {
    (void)ctx;

    int res = 0;

    res |= s_test_known_crc32(allocator, CRC_FUNC_NAME(s_crc32_reference));
    res |= s_test_known_crc32(allocator, CRC_FUNC_NAME(aws_checksums_crc32_sw));
    res |= s_test_known_crc32(allocator, CRC_FUNC_NAME(aws_checksums_crc32));

    return res;
}
AWS_TEST_CASE(test_crc32, s_test_crc32)

static int s_test_large_buffer_crc32(struct aws_allocator *allocator, void *ctx) {
    (void)ctx;
#if SIZE_BITS == 32 || defined(__OpenBSD__) /* openbsd fails to allocate big buffer */
    (void)allocator;
    return AWS_OP_SKIP;
#else
    const size_t len = 3 * 1024 * 1024 * 1024ULL;
    const uint8_t *many_zeroes = aws_mem_calloc(allocator, len, sizeof(uint8_t));
    uint32_t result = aws_checksums_crc32_ex(many_zeroes, len, 0);
    aws_mem_release(allocator, (void *)many_zeroes);
    ASSERT_HEX_EQUALS(0x480BBE37, result);
    return AWS_OP_SUCCESS;
#endif
}
AWS_TEST_CASE(test_large_buffer_crc32, s_test_large_buffer_crc32)