File size: 8,912 Bytes
e996a55
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#include "unity/unity.h"
#include "zlib.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>

/* Helpers to create and clean up temporary files */
static char *make_temp_path(void) {
    char tmpl[] = "/tmp/zlib_gzoffset64_XXXXXX";
    int fd = mkstemp(tmpl);
    if (fd == -1) {
        return NULL;
    }
    close(fd); /* We'll reopen as needed */
    char *path = (char *)malloc(strlen(tmpl) + 1);
    if (!path) return NULL;
    strcpy(path, tmpl);
    return path;
}

static size_t file_size(const char *path) {
    struct stat st;
    if (stat(path, &st) != 0) return 0;
    return (size_t)st.st_size;
}

/* Write a gzip file with given data bytes */
static int write_gz_file(const char *path, const unsigned char *data, size_t len) {
    gzFile gz = gzopen(path, "wb");
    if (!gz) return -1;
    size_t total = 0;
    while (total < len) {
        unsigned chunk = (unsigned)((len - total) > 4096 ? 4096 : (len - total));
        int wrote = gzwrite(gz, data + total, chunk);
        if (wrote <= 0) {
            gzclose(gz);
            return -1;
        }
        total += (size_t)wrote;
    }
    int rc = gzclose(gz);
    return rc == Z_OK ? 0 : -1;
}

/* Generate repetitive data */
static unsigned char *make_data(size_t len) {
    unsigned char *buf = (unsigned char *)malloc(len);
    if (!buf) return NULL;
    for (size_t i = 0; i < len; i++) {
        buf[i] = (unsigned char)(i * 1315423911u + 0x5bd1e995u); /* simple pattern */
    }
    return buf;
}

void setUp(void) {
    /* Setup code here, or leave empty */
}

void tearDown(void) {
    /* Cleanup code here, or leave empty */
}

/* Test cases */

void test_gzoffset64_null_returns_minus1(void) {
    z_off64_t off = gzoffset64(NULL);
    TEST_ASSERT_EQUAL_INT64(-1, off);
}

void test_gzoffset64_write_mode_zero_and_increases_after_flush(void) {
    char *path = make_temp_path();
    TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path");

    gzFile gz = gzopen(path, "wb");
    TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzopen failed for write");

    /* Initially zero */
    z_off64_t off0 = gzoffset64(gz);
    TEST_ASSERT_EQUAL_INT64(0, off0);

    /* Write some data */
    const char *msg1 = "The quick brown fox jumps over the lazy dog.\n";
    int w1 = gzwrite(gz, msg1, (unsigned)strlen(msg1));
    TEST_ASSERT_TRUE(w1 > 0);

    /* Before flush, offset may still be 0 due to buffering; don't assert > 0 yet */
    z_off64_t off_pre = gzoffset64(gz);
    TEST_ASSERT_TRUE(off_pre >= 0);

    /* Flush to force writes to underlying fd */
    int fr = gzflush(gz, Z_SYNC_FLUSH);
    TEST_ASSERT_TRUE(fr == Z_OK || fr == Z_BUF_ERROR); /* Z_BUF_ERROR means nothing to flush */

    z_off64_t off1 = gzoffset64(gz);
    TEST_ASSERT_TRUE_MESSAGE(off1 >= 0, "gzoffset64 should be non-negative after flush");
    /* With some data written and flushed, we expect some bytes written */
    TEST_ASSERT_TRUE_MESSAGE(off1 > 0, "gzoffset64 should be > 0 after writing and flushing");

    /* Write more and flush again */
    const char *msg2 = "Pack my box with five dozen liquor jugs.\n";
    int w2 = gzwrite(gz, msg2, (unsigned)strlen(msg2));
    TEST_ASSERT_TRUE(w2 > 0);

    fr = gzflush(gz, Z_SYNC_FLUSH);
    TEST_ASSERT_TRUE(fr == Z_OK || fr == Z_BUF_ERROR);

    z_off64_t off2 = gzoffset64(gz);
    TEST_ASSERT_TRUE_MESSAGE(off2 >= off1, "Offset should be non-decreasing after additional writes");

    /* Close and check final size is at least as large as last observed offset */
    int rc = gzclose(gz);
    TEST_ASSERT_EQUAL_INT(Z_OK, rc);

    size_t final_size = file_size(path);
    TEST_ASSERT_TRUE_MESSAGE((z_off64_t)final_size >= off2,
                             "Final file size should be >= offset observed before close");

    /* Cleanup */
    remove(path);
    free(path);
}

void test_gzoffset64_read_mode_initial_fd_position_via_gzdopen(void) {
    char *path = make_temp_path();
    TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path");

    /* Create a regular file with some content */
    int fd_create = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
    TEST_ASSERT_TRUE_MESSAGE(fd_create >= 0, "open for create failed");
    unsigned char zeros[1024];
    memset(zeros, 0, sizeof(zeros));
    ssize_t wr = write(fd_create, zeros, sizeof(zeros));
    TEST_ASSERT_TRUE_MESSAGE(wr == (ssize_t)sizeof(zeros), "write failed");
    close(fd_create);

    /* Reopen for read and position to 123 */
    int fd = open(path, O_RDONLY);
    TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "open for read failed");
    off_t want = 123;
    off_t pos = lseek(fd, want, SEEK_SET);
    TEST_ASSERT_EQUAL_INT64((long long)want, (long long)pos);

    /* Wrap with gzdopen in read mode */
    gzFile gz = gzdopen(fd, "rb");
    TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzdopen failed");

    /* Before any reads, avail_in == 0, so gzoffset64 should reflect current fd pos */
    z_off64_t off = gzoffset64(gz);
    TEST_ASSERT_EQUAL_INT64((long long)want, (long long)off);

    int rc = gzclose(gz); /* closes fd as well */
    TEST_ASSERT_EQUAL_INT(Z_OK, rc);

    remove(path);
    free(path);
}

void test_gzoffset64_read_mode_not_greater_than_raw_fd_position_after_read(void) {
    char *path = make_temp_path();
    TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path");

    /* Create a valid gzip file with some data */
    size_t data_len = 20000;
    unsigned char *data = make_data(data_len);
    TEST_ASSERT_NOT_NULL_MESSAGE(data, "Failed to allocate data");
    TEST_ASSERT_EQUAL_INT(0, write_gz_file(path, data, data_len));
    free(data);

    /* Open raw fd and wrap with gzdopen */
    int fd = open(path, O_RDONLY);
    TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "open gzip for read failed");
    gzFile gz = gzdopen(fd, "rb");
    TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzdopen failed");

    /* Initial checks */
    off_t raw0 = lseek(fd, 0, SEEK_CUR);
    TEST_ASSERT_EQUAL_INT64(0, (long long)raw0);
    z_off64_t off0 = gzoffset64(gz);
    TEST_ASSERT_EQUAL_INT64(0, (long long)off0);

    unsigned char buf[128];
    int rd1 = gzread(gz, buf, sizeof(buf));
    TEST_ASSERT_TRUE_MESSAGE(rd1 > 0, "gzread should read some bytes");

    off_t raw1 = lseek(fd, 0, SEEK_CUR);
    z_off64_t off1 = gzoffset64(gz);
    TEST_ASSERT_TRUE_MESSAGE(off1 >= 0, "gzoffset64 should be non-negative");
    TEST_ASSERT_TRUE_MESSAGE(off1 <= (z_off64_t)raw1, "gzoffset64 should be <= raw fd position after read");

    /* Read more and verify monotonicity and <= raw fd pos */
    int rd2 = gzread(gz, buf, sizeof(buf));
    TEST_ASSERT_TRUE_MESSAGE(rd2 >= 0, "gzread should not error");
    off_t raw2 = lseek(fd, 0, SEEK_CUR);
    z_off64_t off2 = gzoffset64(gz);
    TEST_ASSERT_TRUE_MESSAGE(off2 >= off1, "gzoffset64 should be non-decreasing after additional reads");
    TEST_ASSERT_TRUE_MESSAGE(off2 <= (z_off64_t)raw2, "gzoffset64 should be <= raw fd position after more reads");

    int rc = gzclose(gz);
    TEST_ASSERT_EQUAL_INT(Z_OK, rc);

    remove(path);
    free(path);
}

void test_gzoffset64_unchanged_by_pending_seek_request(void) {
    /* Prepare gzip file */
    char *path = make_temp_path();
    TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp path");

    size_t data_len = 10000;
    unsigned char *data = make_data(data_len);
    TEST_ASSERT_NOT_NULL_MESSAGE(data, "Failed to allocate data");
    TEST_ASSERT_EQUAL_INT(0, write_gz_file(path, data, data_len));
    free(data);

    /* Open via fd and wrap in gzdopen */
    int fd = open(path, O_RDONLY);
    TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "open for read failed");
    gzFile gz = gzdopen(fd, "rb");
    TEST_ASSERT_NOT_NULL_MESSAGE(gz, "gzdopen failed");

    /* Read a bit to ensure buffers are initialized */
    unsigned char tmp[64];
    int rd = gzread(gz, tmp, sizeof(tmp));
    TEST_ASSERT_TRUE_MESSAGE(rd >= 0, "gzread should not fail");

    z_off64_t before = gzoffset64(gz);

    /* Issue a pending relative seek (SEEK_CUR) which sets state->seek without moving OS fd immediately */
    z_off64_t newpos = gzseek64(gz, 10, SEEK_CUR);
    TEST_ASSERT_TRUE_MESSAGE(newpos >= 0, "gzseek64 should succeed");

    z_off64_t after = gzoffset64(gz);

    /* gzoffset64 should reflect current fd - avail_in, not the pending skip */
    TEST_ASSERT_EQUAL_INT64((long long)before, (long long)after);

    int rc = gzclose(gz);
    TEST_ASSERT_EQUAL_INT(Z_OK, rc);

    remove(path);
    free(path);
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_gzoffset64_null_returns_minus1);
    RUN_TEST(test_gzoffset64_write_mode_zero_and_increases_after_flush);
    RUN_TEST(test_gzoffset64_read_mode_initial_fd_position_via_gzdopen);
    RUN_TEST(test_gzoffset64_read_mode_not_greater_than_raw_fd_position_after_read);
    RUN_TEST(test_gzoffset64_unchanged_by_pending_seek_request);
    return UNITY_END();
}