chunkset_tpl.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. #include "zbuild.h"
  5. #include <stdlib.h>
  6. #if CHUNK_SIZE == 32 && defined(X86_SSSE3)
  7. extern uint8_t* chunkmemset_ssse3(uint8_t *out, unsigned dist, unsigned len);
  8. #endif
  9. /* Returns the chunk size */
  10. Z_INTERNAL uint32_t CHUNKSIZE(void) {
  11. return sizeof(chunk_t);
  12. }
  13. /* Behave like memcpy, but assume that it's OK to overwrite at least
  14. chunk_t bytes of output even if the length is shorter than this,
  15. that the length is non-zero, and that `from` lags `out` by at least
  16. sizeof chunk_t bytes (or that they don't overlap at all or simply that
  17. the distance is less than the length of the copy).
  18. Aside from better memory bus utilisation, this means that short copies
  19. (chunk_t bytes or fewer) will fall straight through the loop
  20. without iteration, which will hopefully make the branch prediction more
  21. reliable. */
  22. #ifndef HAVE_CHUNKCOPY
  23. static inline uint8_t* CHUNKCOPY(uint8_t *out, uint8_t const *from, unsigned len) {
  24. Assert(len > 0, "chunkcopy should never have a length 0");
  25. chunk_t chunk;
  26. int32_t align = ((len - 1) % sizeof(chunk_t)) + 1;
  27. loadchunk(from, &chunk);
  28. storechunk(out, &chunk);
  29. out += align;
  30. from += align;
  31. len -= align;
  32. while (len > 0) {
  33. loadchunk(from, &chunk);
  34. storechunk(out, &chunk);
  35. out += sizeof(chunk_t);
  36. from += sizeof(chunk_t);
  37. len -= sizeof(chunk_t);
  38. }
  39. return out;
  40. }
  41. #endif
  42. /* Perform short copies until distance can be rewritten as being at least
  43. sizeof chunk_t.
  44. This assumes that it's OK to overwrite at least the first
  45. 2*sizeof(chunk_t) bytes of output even if the copy is shorter than this.
  46. This assumption holds because inflate_fast() starts every iteration with at
  47. least 258 bytes of output space available (258 being the maximum length
  48. output from a single token; see inflate_fast()'s assumptions below). */
  49. #ifndef HAVE_CHUNKUNROLL
  50. static inline uint8_t* CHUNKUNROLL(uint8_t *out, unsigned *dist, unsigned *len) {
  51. unsigned char const *from = out - *dist;
  52. chunk_t chunk;
  53. while (*dist < *len && *dist < sizeof(chunk_t)) {
  54. loadchunk(from, &chunk);
  55. storechunk(out, &chunk);
  56. out += *dist;
  57. *len -= *dist;
  58. *dist += *dist;
  59. }
  60. return out;
  61. }
  62. #endif
  63. #ifndef HAVE_CHUNK_MAG
  64. /* Loads a magazine to feed into memory of the pattern */
  65. static inline chunk_t GET_CHUNK_MAG(uint8_t *buf, uint32_t *chunk_rem, uint32_t dist) {
  66. /* This code takes string of length dist from "from" and repeats
  67. * it for as many times as can fit in a chunk_t (vector register) */
  68. uint32_t cpy_dist;
  69. uint32_t bytes_remaining = sizeof(chunk_t);
  70. chunk_t chunk_load;
  71. uint8_t *cur_chunk = (uint8_t *)&chunk_load;
  72. while (bytes_remaining) {
  73. cpy_dist = MIN(dist, bytes_remaining);
  74. memcpy(cur_chunk, buf, cpy_dist);
  75. bytes_remaining -= cpy_dist;
  76. cur_chunk += cpy_dist;
  77. /* This allows us to bypass an expensive integer division since we're effectively
  78. * counting in this loop, anyway */
  79. *chunk_rem = cpy_dist;
  80. }
  81. return chunk_load;
  82. }
  83. #endif
  84. /* Copy DIST bytes from OUT - DIST into OUT + DIST * k, for 0 <= k < LEN/DIST.
  85. Return OUT + LEN. */
  86. Z_INTERNAL uint8_t* CHUNKMEMSET(uint8_t *out, unsigned dist, unsigned len) {
  87. /* Debug performance related issues when len < sizeof(uint64_t):
  88. Assert(len >= sizeof(uint64_t), "chunkmemset should be called on larger chunks"); */
  89. Assert(dist > 0, "chunkmemset cannot have a distance 0");
  90. /* Only AVX2 */
  91. #if CHUNK_SIZE == 32 && defined(X86_SSSE3)
  92. if (len <= 16) {
  93. return chunkmemset_ssse3(out, dist, len);
  94. }
  95. #endif
  96. uint8_t *from = out - dist;
  97. if (dist == 1) {
  98. memset(out, *from, len);
  99. return out + len;
  100. } else if (dist > sizeof(chunk_t)) {
  101. return CHUNKCOPY(out, out - dist, len);
  102. }
  103. chunk_t chunk_load;
  104. uint32_t chunk_mod = 0;
  105. /* TODO: possibly build up a permutation table for this if not an even modulus */
  106. #ifdef HAVE_CHUNKMEMSET_2
  107. if (dist == 2) {
  108. chunkmemset_2(from, &chunk_load);
  109. } else
  110. #endif
  111. #ifdef HAVE_CHUNKMEMSET_4
  112. if (dist == 4) {
  113. chunkmemset_4(from, &chunk_load);
  114. } else
  115. #endif
  116. #ifdef HAVE_CHUNKMEMSET_8
  117. if (dist == 8) {
  118. chunkmemset_8(from, &chunk_load);
  119. } else if (dist == sizeof(chunk_t)) {
  120. loadchunk(from, &chunk_load);
  121. } else
  122. #endif
  123. {
  124. chunk_load = GET_CHUNK_MAG(from, &chunk_mod, dist);
  125. }
  126. /* If we're lucky enough and dist happens to be an even modulus of our vector length,
  127. * we can do two stores per loop iteration, which for most ISAs, especially x86, is beneficial */
  128. if (chunk_mod == 0) {
  129. while (len >= (2 * sizeof(chunk_t))) {
  130. storechunk(out, &chunk_load);
  131. storechunk(out + sizeof(chunk_t), &chunk_load);
  132. out += 2 * sizeof(chunk_t);
  133. len -= 2 * sizeof(chunk_t);
  134. }
  135. }
  136. /* If we don't have a "dist" length that divides evenly into a vector
  137. * register, we can write the whole vector register but we need only
  138. * advance by the amount of the whole string that fits in our chunk_t.
  139. * If we do divide evenly into the vector length, adv_amount = chunk_t size*/
  140. uint32_t adv_amount = sizeof(chunk_t) - chunk_mod;
  141. while (len >= sizeof(chunk_t)) {
  142. storechunk(out, &chunk_load);
  143. len -= adv_amount;
  144. out += adv_amount;
  145. }
  146. if (len) {
  147. memcpy(out, &chunk_load, len);
  148. out += len;
  149. }
  150. return out;
  151. }
  152. Z_INTERNAL uint8_t* CHUNKMEMSET_SAFE(uint8_t *out, unsigned dist, unsigned len, unsigned left) {
  153. #if !defined(UNALIGNED64_OK)
  154. # if !defined(UNALIGNED_OK)
  155. static const uint32_t align_mask = 7;
  156. # else
  157. static const uint32_t align_mask = 3;
  158. # endif
  159. #endif
  160. len = MIN(len, left);
  161. uint8_t *from = out - dist;
  162. #if !defined(UNALIGNED64_OK)
  163. while (((uintptr_t)out & align_mask) && (len > 0)) {
  164. *out++ = *from++;
  165. --len;
  166. --left;
  167. }
  168. #endif
  169. if (left < (unsigned)(3 * sizeof(chunk_t))) {
  170. while (len > 0) {
  171. *out++ = *from++;
  172. --len;
  173. }
  174. return out;
  175. }
  176. if (len)
  177. return CHUNKMEMSET(out, dist, len);
  178. return out;
  179. }