summaryrefslogtreecommitdiffstats
path: root/lib/mem.c
blob: 68103457b655f4210e93d0d476fda38314eed661 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2007 Ronald G. Minnich <rminnich@gmail.com>
 * Copyright (C) 2007 Peter Stuge <peter@stuge.se>
 * Copyright (C) 2007 Uwe Hermann <uwe@hermann-uwe.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
 */

/* Memory routines with some optimizations. Please don't be silly and inline
 * these. Inlines are not as wonderful as people think.
 */

#include <types.h>
#include <string.h>

/**
 * memcpy() and memmove() helper that uses unsigned long copying when dest and
 * src are both unsigned long aligned, or offset by the same amount from being
 * unsigned long aligned.
 *
 * Copies data one byte at a time until both dest and src are aligned to
 * sizeof(unsigned long) or until no data is left.
 *
 * Copies data one unsigned long at a time until fewer than
 * sizeof(unsigned long) bytes remain.
 *
 * Finally, or if dest and src have different alignment, copies any remaining
 * data one byte at a time.
 *
 * @param dest Memory area to copy to.
 * @param src Memory area to copy from.
 * @param len Number of bytes to copy.
 * @param backwards Start at the end, used by memmove() when dest > src.
 */
static void memcpy_helper(void *dest, const void *src, size_t len,
			  int backwards)
{
	u8 *d = dest;
	const u8 *s = src;
	unsigned long *ld;
	const unsigned long *ls;
	u8 longmask = sizeof(unsigned long) - 1;
	u8 longlen = sizeof(unsigned long);

	if (d == s || !len)
		return;

	if (backwards) {
		d += len;
		s += len;
		if (((unsigned long)d & longmask) ==
		    ((unsigned long)s & longmask)) {
			while (((unsigned long)d & longmask
				|| (unsigned long)s & longmask) && len) {
				*--d = *--s;
				len--;
			}
			ld = (unsigned long *)d;
			ls = (const unsigned long *)s;
			while (len >= longlen) {
				*--ld = *--ls;
				len -= longlen;
			}
			d = (u8 *) ld;
			s = (const u8 *)ls;
		}
		while (len--)
			*--d = *--s;
	} else {
		if (((unsigned long)d & longmask) ==
		    ((unsigned long)s & longmask)) {
			while (((unsigned long)d & longmask
				|| (unsigned long)s & longmask) && len) {
				*d++ = *s++;
				len--;
			}
			ld = (unsigned long *)d;
			ls = (const unsigned long *)s;
			while (len >= longlen) {
				*ld++ = *ls++;
				len -= longlen;
			}
			d = (u8 *) ld;
			s = (const u8 *)ls;
		}
		while (len--)
			*d++ = *s++;
	}
}

/**
 * Copy 'len' bytes from one memory area to another.
 *
 * The memory areas may _not_ overlap.
 *
 * @param dest Pointer to the destination memory area.
 * @param src Pointer to the source memory area.
 * @param len Number of bytes to copy.
 * @return Pointer specified by parameter dest
 */
void *memcpy(void *dest, const void *src, size_t len)
{
	memcpy_helper(dest, src, len, 0);
	return dest;
}

/**
 * Copy 'len' bytes from one memory area to another.
 *
 * The memory areas may overlap.
 *
 * @param dest Pointer to the destination memory area.
 * @param src Pointer to the source memory area.
 * @param len Number of bytes to copy.
 * @return Pointer specified by parameter dest
 */
void *memmove(void *dest, const void *src, size_t len)
{
	memcpy_helper(dest, src, len, dest > src && dest < (src + len));
	return dest;
}

/**
 * Fill a memory area with the specified byte.
 *
 * @param s Pointer to the beginning of the memory area.
 * @param c The byte which is used for filling the memory area.
 * @param len The number of bytes to write.
 * @return Pointer specified by parameter s 
 */
void *memset(void *s, int c, size_t len)
{
	unsigned char *cp = s;
	while (len--)
		*cp++ = (unsigned char)c;
	return s;
}

/**
 * Compare the first 'len' bytes of two memory areas.
 *
 * We assume unsigned characters here.
 *
 * @param s1 Pointer to the first memory area.
 * @param s2 Pointer to the second memory area.
 * @param len Number of bytes to compare.
 * @return Returns a negative number if s1 is shorter than s2. Returns zero if
 * 	   s1 matches s2. Returns a positive number if s1 is longer than s2.
 */
int memcmp(const void *s1, const void *s2, size_t len)
{
	const unsigned char *d = (const unsigned char *)s1;
	const unsigned char *s = (const unsigned char *)s2;
	while (len--) {
		if (*d < *s)
			return -1;
		if (*d > *s)
			return 1;
		d++, s++;
	}
	return 0;
}