• 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         if (len & 8) {
42             memcpy(out, from, 8);
43             out += 8;
44             from += 8;
45         }
46         if (len & 4) {
47             memcpy(out, from, 4);
48             out += 4;
49             from += 4;
50         }
51         if (len & 2) {
52             memcpy(out, from, 2);
53             out += 2;
54             from += 2;
55         }
56         if (len & 1) {
57             *out++ = *from++;
58         }
59         return out;
60     }
61     return CHUNKCOPY(out, from, len);
62 }
63 
64 /* Perform short copies until distance can be rewritten as being at least
65    sizeof chunk_t.
66 
67    This assumes that it's OK to overwrite at least the first
68    2*sizeof(chunk_t) bytes of output even if the copy is shorter than this.
69    This assumption holds because inflate_fast() starts every iteration with at
70    least 258 bytes of output space available (258 being the maximum length
71    output from a single token; see inflate_fast()'s assumptions below). */
CHUNKUNROLL(uint8_t * out,unsigned * dist,unsigned * len)72 Z_INTERNAL uint8_t* CHUNKUNROLL(uint8_t *out, unsigned *dist, unsigned *len) {
73     unsigned char const *from = out - *dist;
74     chunk_t chunk;
75     while (*dist < *len && *dist < sizeof(chunk_t)) {
76         loadchunk(from, &chunk);
77         storechunk(out, &chunk);
78         out += *dist;
79         *len -= *dist;
80         *dist += *dist;
81     }
82     return out;
83 }
84 
85 /* Copy DIST bytes from OUT - DIST into OUT + DIST * k, for 0 <= k < LEN/DIST.
86    Return OUT + LEN. */
CHUNKMEMSET(uint8_t * out,unsigned dist,unsigned len)87 Z_INTERNAL uint8_t* CHUNKMEMSET(uint8_t *out, unsigned dist, unsigned len) {
88     /* Debug performance related issues when len < sizeof(uint64_t):
89        Assert(len >= sizeof(uint64_t), "chunkmemset should be called on larger chunks"); */
90     Assert(dist > 0, "cannot have a distance 0");
91 
92     unsigned char *from = out - dist;
93     chunk_t chunk;
94     unsigned sz = sizeof(chunk);
95     if (len < sz) {
96         do {
97             *out++ = *from++;
98             --len;
99         } while (len != 0);
100         return out;
101     }
102 
103 #ifdef HAVE_CHUNKMEMSET_1
104     if (dist == 1) {
105         chunkmemset_1(from, &chunk);
106     } else
107 #endif
108 #ifdef HAVE_CHUNKMEMSET_2
109     if (dist == 2) {
110         chunkmemset_2(from, &chunk);
111     } else
112 #endif
113 #ifdef HAVE_CHUNKMEMSET_3
114     if (dist == 3) {
115         return chunkmemset_3(out, from, dist, len);
116     } else
117 #endif
118 #ifdef HAVE_CHUNKMEMSET_4
119     if (dist == 4) {
120         chunkmemset_4(from, &chunk);
121     } else
122 #endif
123 #ifdef HAVE_CHUNKMEMSET_6
124     if (dist == 6) {
125         return chunkmemset_6(out, from, dist, len);
126     } else
127 #endif
128 #ifdef HAVE_CHUNKMEMSET_8
129     if (dist == 8) {
130         chunkmemset_8(from, &chunk);
131     } else
132 #endif
133     if (dist == sz) {
134         loadchunk(from, &chunk);
135     } else {
136         out = CHUNKUNROLL(out, &dist, &len);
137         return CHUNKCOPY(out, out - dist, len);
138     }
139 
140     unsigned rem = len % sz;
141     len -= rem;
142     while (len) {
143         storechunk(out, &chunk);
144         out += sz;
145         len -= sz;
146     }
147 
148     /* Last, deal with the case when LEN is not a multiple of SZ. */
149     if (rem)
150         memcpy(out, from, rem);
151     out += rem;
152 
153     return out;
154 }
155 
CHUNKMEMSET_SAFE(uint8_t * out,unsigned dist,unsigned len,unsigned left)156 Z_INTERNAL uint8_t* CHUNKMEMSET_SAFE(uint8_t *out, unsigned dist, unsigned len, unsigned left) {
157     if (left < (unsigned)(3 * sizeof(chunk_t))) {
158         while (len > 0) {
159             *out = *(out - dist);
160             out++;
161             --len;
162         }
163         return out;
164     }
165     return CHUNKMEMSET(out, dist, len);
166 }
167