string.c 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * String functions optimized for hardware which doesn't
  4. * handle unaligned memory accesses efficiently.
  5. *
  6. * Copyright (C) 2021 Matteo Croce
  7. */
  8. #include <linux/types.h>
  9. #include <linux/module.h>
  10. /* Minimum size for a word copy to be convenient */
  11. #define BYTES_LONG sizeof(long)
  12. #define WORD_MASK (BYTES_LONG - 1)
  13. #define MIN_THRESHOLD (BYTES_LONG * 2)
  14. /* convenience union to avoid cast between different pointer types */
  15. union types {
  16. u8 *as_u8;
  17. unsigned long *as_ulong;
  18. uintptr_t as_uptr;
  19. };
  20. union const_types {
  21. const u8 *as_u8;
  22. unsigned long *as_ulong;
  23. uintptr_t as_uptr;
  24. };
  25. void *memcpy(void *dest, const void *src, size_t count)
  26. {
  27. union const_types s = { .as_u8 = src };
  28. union types d = { .as_u8 = dest };
  29. int distance = 0;
  30. if (count < MIN_THRESHOLD)
  31. goto copy_remainder;
  32. /* Copy a byte at time until destination is aligned. */
  33. for (; d.as_uptr & WORD_MASK; count--)
  34. *d.as_u8++ = *s.as_u8++;
  35. distance = s.as_uptr & WORD_MASK;
  36. if (distance) {
  37. unsigned long last, next;
  38. /*
  39. * s is distance bytes ahead of d, and d just reached
  40. * the alignment boundary. Move s backward to word align it
  41. * and shift data to compensate for distance, in order to do
  42. * word-by-word copy.
  43. */
  44. s.as_u8 -= distance;
  45. next = s.as_ulong[0];
  46. for (; count >= BYTES_LONG; count -= BYTES_LONG) {
  47. last = next;
  48. next = s.as_ulong[1];
  49. d.as_ulong[0] = last >> (distance * 8) |
  50. next << ((BYTES_LONG - distance) * 8);
  51. d.as_ulong++;
  52. s.as_ulong++;
  53. }
  54. /* Restore s with the original offset. */
  55. s.as_u8 += distance;
  56. } else {
  57. /*
  58. * If the source and dest lower bits are the same, do a simple
  59. * 32/64 bit wide copy.
  60. */
  61. for (; count >= BYTES_LONG; count -= BYTES_LONG)
  62. *d.as_ulong++ = *s.as_ulong++;
  63. }
  64. copy_remainder:
  65. while (count--)
  66. *d.as_u8++ = *s.as_u8++;
  67. return dest;
  68. }
  69. EXPORT_SYMBOL(memcpy);
  70. /*
  71. * Simply check if the buffer overlaps an call memcpy() in case,
  72. * otherwise do a simple one byte at time backward copy.
  73. */
  74. void *memmove(void *dest, const void *src, size_t count)
  75. {
  76. if (dest < src || src + count <= dest)
  77. return memcpy(dest, src, count);
  78. if (dest > src) {
  79. const char *s = src + count;
  80. char *tmp = dest + count;
  81. while (count--)
  82. *--tmp = *--s;
  83. }
  84. return dest;
  85. }
  86. EXPORT_SYMBOL(memmove);
  87. void *memset(void *s, int c, size_t count)
  88. {
  89. union types dest = { .as_u8 = s };
  90. if (count >= MIN_THRESHOLD) {
  91. unsigned long cu = (unsigned long)c;
  92. /* Compose an ulong with 'c' repeated 4/8 times */
  93. cu |= cu << 8;
  94. cu |= cu << 16;
  95. /* Suppress warning on 32 bit machines */
  96. cu |= (cu << 16) << 16;
  97. for (; count && dest.as_uptr & WORD_MASK; count--)
  98. *dest.as_u8++ = c;
  99. /* Copy using the largest size allowed */
  100. for (; count >= BYTES_LONG; count -= BYTES_LONG)
  101. *dest.as_ulong++ = cu;
  102. }
  103. /* copy the remainder */
  104. while (count--)
  105. *dest.as_u8++ = c;
  106. return s;
  107. }
  108. EXPORT_SYMBOL(memset);