• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* chunkset_tpl.h -- inline functions to copy small data chunks.
2  * For conditions of distribution and use, see copyright notice in zlib.h
3  */
4 
5 /* Returns the chunk size */
CHUNKSIZE(void)6 Z_INTERNAL uint32_t CHUNKSIZE(void) {
7     return sizeof(chunk_t);
8 }
9 
10 /* Behave like memcpy, but assume that it's OK to overwrite at least
11    chunk_t bytes of output even if the length is shorter than this,
12    that the length is non-zero, and that `from` lags `out` by at least
13    sizeof chunk_t bytes (or that they don't overlap at all or simply that
14    the distance is less than the length of the copy).
15 
16    Aside from better memory bus utilisation, this means that short copies
17    (chunk_t bytes or fewer) will fall straight through the loop
18    without iteration, which will hopefully make the branch prediction more
19    reliable. */
CHUNKCOPY(uint8_t * out,uint8_t const * from,unsigned len)20 Z_INTERNAL uint8_t* CHUNKCOPY(uint8_t *out, uint8_t const *from, unsigned len) {
21     chunk_t chunk;
22     --len;
23     loadchunk(from, &chunk);
24     storechunk(out, &chunk);
25     out += (len % sizeof(chunk_t)) + 1;
26     from += (len % sizeof(chunk_t)) + 1;
27     len /= sizeof(chunk_t);
28     while (len > 0) {
29         loadchunk(from, &chunk);
30         storechunk(out, &chunk);
31         out += sizeof(chunk_t);
32         from += sizeof(chunk_t);
33         --len;
34     }
35     return out;
36 }
37 
38 /* Behave like chunkcopy, but avoid writing beyond of legal output. */
CHUNKCOPY_SAFE(uint8_t * out,uint8_t const * from,unsigned len,uint8_t * safe)39 Z_INTERNAL uint8_t* CHUNKCOPY_SAFE(uint8_t *out, uint8_t const *from, unsigned len, uint8_t *safe) {
40     if ((safe - out) < (ptrdiff_t)sizeof(chunk_t)) {
41         int32_t use_chunk16 = sizeof(chunk_t) > 16 && (len & 16);
42         if (use_chunk16) {
43             memcpy(out, from, 16);
44             out += 16;
45             from += 16;
46         }
47         if (len & 8) {
48             memcpy(out, from, 8);
49             out += 8;
50             from += 8;
51         }
52         if (len & 4) {
53             memcpy(out, from, 4);
54             out += 4;
55             from += 4;
56         }
57         if (len & 2) {
58             memcpy(out, from, 2);
59             out += 2;
60             from += 2;
61         }
62         if (len & 1) {
63             *out++ = *from++;
64         }
65         return out;
66     }
67     return CHUNKCOPY(out, from, len);
68 }
69 
70 /* Perform short copies until distance can be rewritten as being at least
71    sizeof chunk_t.
72 
73    This assumes that it's OK to overwrite at least the first
74    2*sizeof(chunk_t) bytes of output even if the copy is shorter than this.
75    This assumption holds because inflate_fast() starts every iteration with at
76    least 258 bytes of output space available (258 being the maximum length
77    output from a single token; see inflate_fast()'s assumptions below). */
CHUNKUNROLL(uint8_t * out,unsigned * dist,unsigned * len)78 Z_INTERNAL uint8_t* CHUNKUNROLL(uint8_t *out, unsigned *dist, unsigned *len) {
79     unsigned char const *from = out - *dist;
80     chunk_t chunk;
81     while (*dist < *len && *dist < sizeof(chunk_t)) {
82         loadchunk(from, &chunk);
83         storechunk(out, &chunk);
84         out += *dist;
85         *len -= *dist;
86         *dist += *dist;
87     }
88     return out;
89 }
90 
91 /* Copy DIST bytes from OUT - DIST into OUT + DIST * k, for 0 <= k < LEN/DIST.
92    Return OUT + LEN. */
CHUNKMEMSET(uint8_t * out,unsigned dist,unsigned len)93 Z_INTERNAL uint8_t* CHUNKMEMSET(uint8_t *out, unsigned dist, unsigned len) {
94     /* Debug performance related issues when len < sizeof(uint64_t):
95        Assert(len >= sizeof(uint64_t), "chunkmemset should be called on larger chunks"); */
96     Assert(dist > 0, "cannot have a distance 0");
97 
98     unsigned char *from = out - dist;
99     chunk_t chunk;
100     unsigned sz = sizeof(chunk);
101     if (len < sz) {
102         do {
103             *out++ = *from++;
104             --len;
105         } while (len != 0);
106         return out;
107     }
108 
109 #ifdef HAVE_CHUNKMEMSET_1
110     if (dist == 1) {
111         chunkmemset_1(from, &chunk);
112     } else
113 #endif
114 #ifdef HAVE_CHUNKMEMSET_2
115     if (dist == 2) {
116         chunkmemset_2(from, &chunk);
117     } else
118 #endif
119 #ifdef HAVE_CHUNKMEMSET_4
120     if (dist == 4) {
121         chunkmemset_4(from, &chunk);
122     } else
123 #endif
124 #ifdef HAVE_CHUNKMEMSET_8
125     if (dist == 8) {
126         chunkmemset_8(from, &chunk);
127     } else
128 #endif
129     if (dist == sz) {
130         loadchunk(from, &chunk);
131     } else if (dist < sz) {
132         unsigned char *end = out + len - 1;
133         while (len > dist) {
134             out = CHUNKCOPY_SAFE(out, from, dist, end);
135             len -= dist;
136         }
137         if (len > 0) {
138             out = CHUNKCOPY_SAFE(out, from, len, end);
139         }
140         return out;
141     } else {
142         out = CHUNKUNROLL(out, &dist, &len);
143         return CHUNKCOPY(out, out - dist, len);
144     }
145 
146     unsigned rem = len % sz;
147     len -= rem;
148     while (len) {
149         storechunk(out, &chunk);
150         out += sz;
151         len -= sz;
152     }
153 
154     /* Last, deal with the case when LEN is not a multiple of SZ. */
155     if (rem)
156         memcpy(out, from, rem);
157     out += rem;
158 
159     return out;
160 }
161 
CHUNKMEMSET_SAFE(uint8_t * out,unsigned dist,unsigned len,unsigned left)162 Z_INTERNAL uint8_t* CHUNKMEMSET_SAFE(uint8_t *out, unsigned dist, unsigned len, unsigned left) {
163     if (left < (unsigned)(3 * sizeof(chunk_t))) {
164         while (len > 0) {
165             *out = *(out - dist);
166             out++;
167             --len;
168         }
169         return out;
170     }
171     return CHUNKMEMSET(out, dist, len);
172 }
173