6

I want write a repeating pattern of bytes into a block of memory. My idea is to write the first example of the pattern, and then copy it into the rest of the buffer. For example, if I start with this:

ptr: 123400000000

Afterward, I want it to look like this:

ptr: 123412341234

I thought I could use memcpy to write to intersecting regions, like this:

memcpy(ptr + 4, ptr, 8);

The standard does not specify what order the copy will happen in, so if some implementation makes it copy in reverse order, it can give different results:

ptr: 123412340000

or even combined results.

Is there any workaround that lets me still use memcpy, or do I have to implement my own for loop? Note that I cannot use memmove because it does exactly what I'm trying to avoid; it make the ptr be 123412340000, while I want 123412341234.

I program for Mac/iPhone(clang compiler) but a general answer will be good too.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Daniel
  • 30,896
  • 18
  • 85
  • 139
  • 1
    If I understand correctly, you want the pattern between `ptr` and `ptr + 4` to be repeated up to 8 bytes? If so, you won't get what you want from `memmove` nor `memcpy`. – zneak Jun 12 '11 at 21:58
  • @zneak, serial `memcpy` will do that, but I don't know if its serial – Daniel Jun 12 '11 at 22:01
  • @Dani What does "serial" memcpy mean ? – cnicutar Jun 12 '11 at 22:02
  • @Dani, a specific implementation of `memcpy` *might* do this, but such a use case has an undefined behavior. You can roll your own function that does exactly that. – zneak Jun 12 '11 at 22:03
  • 2
    It's actually not a duplicate. I'm voting to reopen; I didn't understand the question at first. – zneak Jun 12 '11 at 22:07
  • @cnicutar, if a byte-per-byte copy happens with his example, the 4 bytes pattern will be repeated on 8 bytes at once, instead of just 4. This is what he wants. – zneak Jun 12 '11 at 22:09
  • The trick that you are trying to use (multiplying a pattern using `memcpy`) is a rather well-known one. However, it doesn't work in accordance with C standard. You cannot use `memcpy` like that in standard C. It is not guaranteed to work. – AnT stands with Russia Jun 12 '11 at 22:15
  • 1
    I'm voting to reopen because OP actually wants something very different than what was asked for. See my comment on Ninefingers' answer. Also -1 to the question for being unclear. – R.. GitHub STOP HELPING ICE Jun 12 '11 at 22:48
  • @AndreyT: Moreover, it makes no sense. What determines the interval at which the pattern propagates? Most likely the optimal unit copying size for the specific machine (not necessarily just the ISA) you run the program on. – R.. GitHub STOP HELPING ICE Jun 12 '11 at 23:00
  • Is the question clearer now, @R..? If not, please edit further. – Rob Kennedy Jun 12 '11 at 23:46

6 Answers6

5

Here is what kernel.org says:

The memcpy() function copies n bytes from memory area src to memory area dest. The memory areas must not overlap. Use memmove(3) if the memory areas do overlap.

An here is what MSDN says:

If the source and destination overlap, the behavior of memcpy is undefined. Use memmove to handle overlapping regions.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
5

There is no standard function to repeat a pattern of bytes upon a memory range. You can use the memset_pattern* function family to get fixed-size patterns; if you need the size to vary, you'll have to roll your own.

// fills the 12 first bytes at `ptr` with the 4 first bytes of `ptr`
memset_pattern4(ptr, ptr, 12);

Be aware that memset_pattern4, memset_pattern8 and memset_pattern16 exist only on Mac OS/iOS, so don't use them for cross-platform development.

Otherwise, rolling a (cross-platform) function that does a byte-per-byte copy is pretty easy.

void byte_copy(void* into, void* from, size_t size)
{
    for (size_t i = 0; i < size; i++)
        into[i] = from[i];
}
zneak
  • 134,922
  • 42
  • 253
  • 328
  • 1
    Looks like you just reimplemented `memcpy()` there, which *is* standard. – Jonathan Grynspan Jun 12 '11 at 23:55
  • 1
    @Jonathan Grynspan There is one subtle difference: this function has a well-defined behavior when `into + size` and `from + size` overlap. `Memcpy`, on the other hand, doesn't. – zneak Jun 13 '11 at 00:04
  • For instance, an optimized `memcpy` implementation could choose to read and write chunks of 4-8 bytes at once to go faster. In such a case, if the memory zones overlap, this optimized implementation wouldn't necessarily behave the same as mine. – zneak Jun 13 '11 at 00:07
  • True, but in that sense this is still `memcpy()`, since the behaviour for `memcpy()` in that case is undefined. – Jonathan Grynspan Jun 13 '11 at 03:37
  • If you simply mean that I implemented a basic version of `memcpy`'s algorithm, then we both agree (though you could not use this implementation as a drop-in replacement for `memcpy`, because it's supposed to return a copy of the original memory). Other than that, my implementation differs from the standard `memcpy` in that its behavior is defined when the memory regions overlap, and we cannot use the standard `memcpy` precisely because it does not have this guarantee. – zneak Jun 13 '11 at 04:00
  • Indeed, I think we agree here. :) – Jonathan Grynspan Jun 13 '11 at 11:55
2

The C++ answer for all platforms is std::fill_n(destination, elementRepeats, elementValue).

For what you've asked for:

short val = 0x1234;
std::fill_n(ptr, 3, val); 

This will work for val of any type; chars, shorts, ints, int64_t, etc.

G Huxley
  • 1,130
  • 14
  • 19
  • `std::fill_n()` takes a reference for its third parameter, not a pointer, so it seems to me this answer is wrong, no? This will fill the memory pointed to by `ptr` with three consecutive copies of the pointer to `val`, not the value of `val`. Or am I mistaken? – bhaller Feb 26 '17 at 07:01
  • @bhaller Thanks for pointing out the mistake in my example. I've fixed it up but, regardless, fill_n is still the right solution. – G Huxley May 29 '17 at 23:23
0

You can do that by copying once, and then memcpy everything to copied to the following bytes and repeat that, it's better understood in code:

void block_memset(void *destination, const void *source, size_t source_size, size_t repeats) {
    memcpy(destination,source,source_size);
    for (size_t i = 1; i < repeats; i += i)
        memcpy(destination + i,destination,source_size * (min(i,repeats - i)));
}

I benchmarked; it's as fast as regular memset for large number of repeats, and the source_size is quite dynamic without much performance penalty too.

LyingOnTheSky
  • 2,844
  • 1
  • 14
  • 33
0

Old answer

You want memmove(). Full description:

The memmove() function shall copy n bytes from the object pointed to by s2 into the object pointed to by s1. Copying takes place as if the n bytes from the object pointed to by s2 are first copied into a temporary array of n bytes that does not overlap the objects pointed to by s1 and s2, and then the n bytes from the temporary array are copied into the object pointed to by s1.

From the memcpy() page:

If copying takes place between objects that overlap, the behaviour is undefined.

You have to use memmove() anyway. This is because the result of using memcpy() is not reliable in any way.

Relevant bits to the actual question

You're asking for memcpy(ptr + 4, ptr, 8); which says copy 8 bytes from ptr and put them at ptr+4. ptr is 123400000000, the first 8 bytes are 1234000, so it is doing this:

Original : 123400000000
Writes   :     12340000
Result   : 123412340000

You'd need to call:

memcpy(ptr+4, ptr, 4);
memcpy(ptr+8, ptr, 4);

To achieve what you're after. Or implement an equivalent. This ought to do it, but it is untested, and is equivalent to memcpy; you'll need to either add the extra temporary buffer or use two non-overlapping areas of memory.

void memexpand(void* result, const void* start, 
               const uint64_t cycle, const uint64_t limit)
{
    uint64_t count = 0;
    uint8_t* source = start;
    uint8_t* dest   = result;

    while ( count < limit )
    {
        *dest = *source;
        dest++;
        count++;

        if ( count % cycle == 0 )
        {
            source = start;
        }
        else
        {
            source++;
        }
    }
}
  • Can't use memmove, it does not product the result I want – Daniel Jun 12 '11 at 21:56
  • @Dani I see what I think you're getting at. You *do* want `memmove`, but you need to use it slightly differently. See edit. –  Jun 12 '11 at 22:04
  • 1
    With OP's changed requirements, `memmove` is not needed. OP merely wants to repeatedly write the same 32-bit (?) pattern across the whole buffer. For this, either a repeated 32-bit `memcpy` (note that regions do not overlap now) for each 32 bits, or `wmemset` (if `sizeof(wchar_t)==4`) will do the job just fine. – R.. GitHub STOP HELPING ICE Jun 12 '11 at 22:47
  • @R.. I've updated my answer, you're right. I've gone with the memcpy repeated, since that's what I had with memmove. –  Jun 12 '11 at 23:01
-1

Why not just allocate an 8 byte buffer, move it there, then move it back to where you want it? (As @cnicutar says, you shouldn't have overlapping address spaces for memcpy.)

Scott C Wilson
  • 19,102
  • 10
  • 61
  • 83