Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * String functions optimized for hardware which doesn't
0004  * handle unaligned memory accesses efficiently.
0005  *
0006  * Copyright (C) 2021 Matteo Croce
0007  */
0008 
0009 #include <linux/types.h>
0010 #include <linux/module.h>
0011 
0012 /* Minimum size for a word copy to be convenient */
0013 #define BYTES_LONG  sizeof(long)
0014 #define WORD_MASK   (BYTES_LONG - 1)
0015 #define MIN_THRESHOLD   (BYTES_LONG * 2)
0016 
0017 /* convenience union to avoid cast between different pointer types */
0018 union types {
0019     u8 *as_u8;
0020     unsigned long *as_ulong;
0021     uintptr_t as_uptr;
0022 };
0023 
0024 union const_types {
0025     const u8 *as_u8;
0026     unsigned long *as_ulong;
0027     uintptr_t as_uptr;
0028 };
0029 
0030 void *memcpy(void *dest, const void *src, size_t count)
0031 {
0032     union const_types s = { .as_u8 = src };
0033     union types d = { .as_u8 = dest };
0034     int distance = 0;
0035 
0036     if (count < MIN_THRESHOLD)
0037         goto copy_remainder;
0038 
0039     /* Copy a byte at time until destination is aligned. */
0040     for (; d.as_uptr & WORD_MASK; count--)
0041         *d.as_u8++ = *s.as_u8++;
0042 
0043     distance = s.as_uptr & WORD_MASK;
0044 
0045     if (distance) {
0046         unsigned long last, next;
0047 
0048         /*
0049          * s is distance bytes ahead of d, and d just reached
0050          * the alignment boundary. Move s backward to word align it
0051          * and shift data to compensate for distance, in order to do
0052          * word-by-word copy.
0053          */
0054         s.as_u8 -= distance;
0055 
0056         next = s.as_ulong[0];
0057         for (; count >= BYTES_LONG; count -= BYTES_LONG) {
0058             last = next;
0059             next = s.as_ulong[1];
0060 
0061             d.as_ulong[0] = last >> (distance * 8) |
0062                 next << ((BYTES_LONG - distance) * 8);
0063 
0064             d.as_ulong++;
0065             s.as_ulong++;
0066         }
0067 
0068         /* Restore s with the original offset. */
0069         s.as_u8 += distance;
0070     } else {
0071         /*
0072          * If the source and dest lower bits are the same, do a simple
0073          * 32/64 bit wide copy.
0074          */
0075         for (; count >= BYTES_LONG; count -= BYTES_LONG)
0076             *d.as_ulong++ = *s.as_ulong++;
0077     }
0078 
0079 copy_remainder:
0080     while (count--)
0081         *d.as_u8++ = *s.as_u8++;
0082 
0083     return dest;
0084 }
0085 EXPORT_SYMBOL(memcpy);
0086 
0087 /*
0088  * Simply check if the buffer overlaps an call memcpy() in case,
0089  * otherwise do a simple one byte at time backward copy.
0090  */
0091 void *memmove(void *dest, const void *src, size_t count)
0092 {
0093     if (dest < src || src + count <= dest)
0094         return memcpy(dest, src, count);
0095 
0096     if (dest > src) {
0097         const char *s = src + count;
0098         char *tmp = dest + count;
0099 
0100         while (count--)
0101             *--tmp = *--s;
0102     }
0103     return dest;
0104 }
0105 EXPORT_SYMBOL(memmove);
0106 
0107 void *memset(void *s, int c, size_t count)
0108 {
0109     union types dest = { .as_u8 = s };
0110 
0111     if (count >= MIN_THRESHOLD) {
0112         unsigned long cu = (unsigned long)c;
0113 
0114         /* Compose an ulong with 'c' repeated 4/8 times */
0115         cu |= cu << 8;
0116         cu |= cu << 16;
0117         /* Suppress warning on 32 bit machines */
0118         cu |= (cu << 16) << 16;
0119 
0120         for (; count && dest.as_uptr & WORD_MASK; count--)
0121             *dest.as_u8++ = c;
0122 
0123         /* Copy using the largest size allowed */
0124         for (; count >= BYTES_LONG; count -= BYTES_LONG)
0125             *dest.as_ulong++ = cu;
0126     }
0127 
0128     /* copy the remainder */
0129     while (count--)
0130         *dest.as_u8++ = c;
0131 
0132     return s;
0133 }
0134 EXPORT_SYMBOL(memset);